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 075a5809bca929fc24560c981888bf739ebd7c07
Author: yangyang zhong <[email protected]>
AuthorDate: Tue Jun 3 09:37:06 2025 +0800

    [#6787] feat(authz): Introduce GravitinoInterceptionService (#7258)
    
    ### What changes were proposed in this pull request?
    
    Introduce GravitinoInterceptionService to implement metadata
    authorization in REST requests through an interceptor.
    
    ### Why are the changes needed?
    
    Fix: #6787
    
    ### Does this PR introduce _any_ user-facing change?
    
    None
    
    ### How was this patch tested?
    
    org.apache.gravitino.server.web.filter.TestGravitinoInterceptionService
---
 .../server/authorization/MetadataFilterHelper.java |  21 ++--
 .../AuthorizationExpressionEvaluator.java          |   7 +-
 .../authorization/jcasbin/JcasbinAuthorizer.java   |   6 +-
 .../TestAuthorizationExpressionEvaluator.java      |  23 ++--
 server/build.gradle.kts                            |   2 +-
 .../apache/gravitino/server/GravitinoServer.java   |   6 ++
 .../web/filter/GravitinoInterceptionService.java   | 102 +++++++++++++++++-
 .../server/web/rest/CatalogOperations.java         |   6 +-
 .../filter/TestGravitinoInterceptionService.java   | 119 +++++++++++++++++++++
 9 files changed, 260 insertions(+), 32 deletions(-)

diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataFilterHelper.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataFilterHelper.java
index ddf25f9b5c..907532af96 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataFilterHelper.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataFilterHelper.java
@@ -22,7 +22,6 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 import org.apache.gravitino.Entity;
-import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.authorization.Privilege;
 import 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionEvaluator;
@@ -84,7 +83,7 @@ public class MetadataFilterHelper {
     return Arrays.stream(nameIdentifiers)
         .filter(
             metaDataName -> {
-              Map<MetadataObject.Type, NameIdentifier> nameIdentifierMap =
+              Map<Entity.EntityType, NameIdentifier> nameIdentifierMap =
                   spiltMetadataNames(metalake, entityType, metaDataName);
               return 
authorizationExpressionEvaluator.evaluate(nameIdentifierMap);
             })
@@ -101,25 +100,25 @@ public class MetadataFilterHelper {
    * @param nameIdentifier metadata name
    * @return A map containing the metadata object and all its parent objects, 
keyed by their types
    */
-  private static Map<MetadataObject.Type, NameIdentifier> spiltMetadataNames(
+  private static Map<Entity.EntityType, NameIdentifier> spiltMetadataNames(
       String metalake, Entity.EntityType entityType, NameIdentifier 
nameIdentifier) {
-    Map<MetadataObject.Type, NameIdentifier> nameIdentifierMap = new 
HashMap<>();
-    nameIdentifierMap.put(MetadataObject.Type.METALAKE, 
NameIdentifierUtil.ofMetalake(metalake));
+    Map<Entity.EntityType, NameIdentifier> nameIdentifierMap = new HashMap<>();
+    nameIdentifierMap.put(Entity.EntityType.METALAKE, 
NameIdentifierUtil.ofMetalake(metalake));
     switch (entityType) {
       case CATALOG:
-        nameIdentifierMap.put(MetadataObject.Type.CATALOG, nameIdentifier);
+        nameIdentifierMap.put(Entity.EntityType.CATALOG, nameIdentifier);
         break;
       case SCHEMA:
-        nameIdentifierMap.put(MetadataObject.Type.SCHEMA, nameIdentifier);
+        nameIdentifierMap.put(Entity.EntityType.SCHEMA, nameIdentifier);
         nameIdentifierMap.put(
-            MetadataObject.Type.CATALOG, 
NameIdentifierUtil.getCatalogIdentifier(nameIdentifier));
+            Entity.EntityType.CATALOG, 
NameIdentifierUtil.getCatalogIdentifier(nameIdentifier));
         break;
       case TABLE:
-        nameIdentifierMap.put(MetadataObject.Type.TABLE, nameIdentifier);
+        nameIdentifierMap.put(Entity.EntityType.TABLE, nameIdentifier);
         nameIdentifierMap.put(
-            MetadataObject.Type.SCHEMA, 
NameIdentifierUtil.getSchemaIdentifier(nameIdentifier));
+            Entity.EntityType.SCHEMA, 
NameIdentifierUtil.getSchemaIdentifier(nameIdentifier));
         nameIdentifierMap.put(
-            MetadataObject.Type.CATALOG, 
NameIdentifierUtil.getCatalogIdentifier(nameIdentifier));
+            Entity.EntityType.CATALOG, 
NameIdentifierUtil.getCatalogIdentifier(nameIdentifier));
         break;
       default:
         break;
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 4106e442fc..f0d4c6bf5b 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
@@ -53,7 +53,7 @@ public class AuthorizationExpressionEvaluator {
    * @param metadataNames key-metadata type, value-metadata NameIdentifier
    * @return authorization result
    */
-  public boolean evaluate(Map<MetadataObject.Type, NameIdentifier> 
metadataNames) {
+  public boolean evaluate(Map<Entity.EntityType, NameIdentifier> 
metadataNames) {
     Principal currentPrincipal = PrincipalUtils.getCurrentPrincipal();
     GravitinoAuthorizer gravitinoAuthorizer =
         GravitinoAuthorizerProvider.getInstance().getGravitinoAuthorizer();
@@ -63,11 +63,10 @@ public class AuthorizationExpressionEvaluator {
     metadataNames.forEach(
         (metadataType, metadataName) -> {
           MetadataObject metadataObject =
-              NameIdentifierUtil.toMetadataObject(
-                  metadataName, 
Entity.EntityType.valueOf(metadataType.name()));
+              NameIdentifierUtil.toMetadataObject(metadataName, metadataType);
           ognlContext.put(metadataType.name(), metadataObject);
         });
-    NameIdentifier nameIdentifier = 
metadataNames.get(MetadataObject.Type.METALAKE);
+    NameIdentifier nameIdentifier = 
metadataNames.get(Entity.EntityType.METALAKE);
     ognlContext.put("METALAKE_NAME", nameIdentifier.name());
     try {
       Object value = Ognl.getValue(ognlAuthorizationExpression, ognlContext);
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
index 0534412be3..dce344053d 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
@@ -47,10 +47,14 @@ import 
org.apache.gravitino.storage.relational.service.UserMetaService;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.casbin.jcasbin.main.Enforcer;
 import org.casbin.jcasbin.model.Model;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /** The Jcasbin implementation of GravitinoAuthorizer. */
 public class JcasbinAuthorizer implements GravitinoAuthorizer {
 
+  private static final Logger LOG = 
LoggerFactory.getLogger(JcasbinAuthorizer.class);
+
   /** Jcasbin enforcer is used for metadata authorization. */
   private Enforcer enforcer;
 
@@ -157,7 +161,7 @@ public class JcasbinAuthorizer implements 
GravitinoAuthorizer {
       }
       // TODO load owner relationship
     } catch (Exception e) {
-      throw new RuntimeException("Can not load privilege", e);
+      LOG.error(e.getMessage(), e);
     }
   }
 
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
index d873ee17e6..21b04532bd 100644
--- 
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
@@ -23,7 +23,7 @@ import static org.mockito.Mockito.when;
 
 import java.util.HashMap;
 import java.util.Map;
-import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.Entity;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.UserPrincipal;
 import org.apache.gravitino.server.authorization.GravitinoAuthorizerProvider;
@@ -52,21 +52,20 @@ public class TestAuthorizationExpressionEvaluator {
       GravitinoAuthorizerProvider mockedProvider = 
mock(GravitinoAuthorizerProvider.class);
       
mockStatic.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
       when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new 
MockGravitinoAuthorizer());
-      Map<MetadataObject.Type, NameIdentifier> metadataNames = new HashMap<>();
+      Map<Entity.EntityType, NameIdentifier> metadataNames = new HashMap<>();
+      metadataNames.put(Entity.EntityType.METALAKE, 
NameIdentifierUtil.ofMetalake("testMetalake"));
       metadataNames.put(
-          MetadataObject.Type.METALAKE, 
NameIdentifierUtil.ofMetalake("testMetalake"));
+          Entity.EntityType.CATALOG, 
NameIdentifierUtil.ofCatalog("testMetalake", "testCatalog"));
       metadataNames.put(
-          MetadataObject.Type.CATALOG, 
NameIdentifierUtil.ofCatalog("testMetalake", "testCatalog"));
-      metadataNames.put(
-          MetadataObject.Type.SCHEMA,
+          Entity.EntityType.SCHEMA,
           NameIdentifierUtil.ofSchema("testMetalake", "testCatalog", 
"testSchema"));
       metadataNames.put(
-          MetadataObject.Type.TABLE,
+          Entity.EntityType.TABLE,
           NameIdentifierUtil.ofTable(
               "testMetalake", "testCatalog", "testSchema", 
"testTableHasNotPermission"));
       
Assertions.assertFalse(authorizationExpressionEvaluator.evaluate(metadataNames));
       metadataNames.put(
-          MetadataObject.Type.TABLE,
+          Entity.EntityType.TABLE,
           NameIdentifierUtil.ofTable("testMetalake", "testCatalog", 
"testSchema", "testTable"));
       
Assertions.assertTrue(authorizationExpressionEvaluator.evaluate(metadataNames));
     }
@@ -86,15 +85,15 @@ public class TestAuthorizationExpressionEvaluator {
       GravitinoAuthorizerProvider mockedProvider = 
mock(GravitinoAuthorizerProvider.class);
       
mockStatic.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
       when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new 
MockGravitinoAuthorizer());
-      Map<MetadataObject.Type, NameIdentifier> metadataNames = new HashMap<>();
+      Map<Entity.EntityType, NameIdentifier> metadataNames = new HashMap<>();
       metadataNames.put(
-          MetadataObject.Type.METALAKE, 
NameIdentifierUtil.ofMetalake("metalakeWithOutOwner"));
+          Entity.EntityType.METALAKE, 
NameIdentifierUtil.ofMetalake("metalakeWithOutOwner"));
       metadataNames.put(
-          MetadataObject.Type.CATALOG,
+          Entity.EntityType.CATALOG,
           NameIdentifierUtil.ofCatalog("metalakeWithOwner", "testCatalog"));
       
Assertions.assertFalse(authorizationExpressionEvaluator.evaluate(metadataNames));
       metadataNames.put(
-          MetadataObject.Type.METALAKE, 
NameIdentifierUtil.ofMetalake("metalakeWithOwner"));
+          Entity.EntityType.METALAKE, 
NameIdentifierUtil.ofMetalake("metalakeWithOwner"));
       
Assertions.assertTrue(authorizationExpressionEvaluator.evaluate(metadataNames));
     }
   }
diff --git a/server/build.gradle.kts b/server/build.gradle.kts
index b58e42773d..5d54834b2d 100644
--- a/server/build.gradle.kts
+++ b/server/build.gradle.kts
@@ -60,7 +60,7 @@ dependencies {
 
   testImplementation(libs.junit.jupiter.api)
   testImplementation(libs.junit.jupiter.params)
-  testImplementation(libs.mockito.core)
+  testImplementation(libs.mockito.inline)
 
   testRuntimeOnly(libs.junit.jupiter.engine)
 }
diff --git 
a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java 
b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
index accf65cb5f..f44f436ad0 100644
--- a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
+++ b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.server;
 import java.io.File;
 import java.util.HashSet;
 import java.util.Properties;
+import javax.inject.Singleton;
 import javax.servlet.Servlet;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.GravitinoEnv;
@@ -46,11 +47,13 @@ import org.apache.gravitino.server.web.JettyServerConfig;
 import org.apache.gravitino.server.web.ObjectMapperProvider;
 import org.apache.gravitino.server.web.VersioningFilter;
 import org.apache.gravitino.server.web.filter.AccessControlNotAllowedFilter;
+import org.apache.gravitino.server.web.filter.GravitinoInterceptionService;
 import org.apache.gravitino.server.web.mapper.JsonMappingExceptionMapper;
 import org.apache.gravitino.server.web.mapper.JsonParseExceptionMapper;
 import org.apache.gravitino.server.web.mapper.JsonProcessingExceptionMapper;
 import org.apache.gravitino.server.web.ui.WebUIFilter;
 import org.apache.gravitino.tag.TagDispatcher;
+import org.glassfish.hk2.api.InterceptionService;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
 import org.glassfish.jersey.CommonProperties;
 import org.glassfish.jersey.jackson.JacksonFeature;
@@ -118,6 +121,9 @@ public class GravitinoServer extends ResourceConfig {
         new AbstractBinder() {
           @Override
           protected void configure() {
+            bind(GravitinoInterceptionService.class)
+                .to(InterceptionService.class)
+                .in(Singleton.class);
             
bind(gravitinoEnv.metalakeDispatcher()).to(MetalakeDispatcher.class).ranked(1);
             
bind(gravitinoEnv.catalogDispatcher()).to(CatalogDispatcher.class).ranked(1);
             
bind(gravitinoEnv.schemaDispatcher()).to(SchemaDispatcher.class).ranked(1);
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 823207af5a..098490d432 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
@@ -18,13 +18,29 @@
 package org.apache.gravitino.server.web.filter;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import org.aopalliance.intercept.ConstructorInterceptor;
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
+import 
org.apache.gravitino.server.authorization.annotations.AuthorizationExpression;
+import 
org.apache.gravitino.server.authorization.annotations.AuthorizationMetadata;
+import 
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionEvaluator;
+import org.apache.gravitino.server.web.Utils;
+import org.apache.gravitino.server.web.rest.CatalogOperations;
+import org.apache.gravitino.utils.NameIdentifierUtil;
+import org.glassfish.hk2.api.Descriptor;
 import org.glassfish.hk2.api.Filter;
 import org.glassfish.hk2.api.InterceptionService;
 
@@ -37,7 +53,7 @@ public class GravitinoInterceptionService implements 
InterceptionService {
 
   @Override
   public Filter getDescriptorFilter() {
-    throw new UnsupportedOperationException();
+    return new 
ClassListFilter(ImmutableSet.of(CatalogOperations.class.getName()));
   }
 
   @Override
@@ -66,7 +82,89 @@ public class GravitinoInterceptionService implements 
InterceptionService {
      */
     @Override
     public Object invoke(MethodInvocation methodInvocation) throws Throwable {
-      throw new UnsupportedOperationException();
+      Method method = methodInvocation.getMethod();
+      Parameter[] parameters = method.getParameters();
+      AuthorizationExpression expressionAnnotation =
+          method.getAnnotation(AuthorizationExpression.class);
+      if (expressionAnnotation != null) {
+        String expression = expressionAnnotation.expression();
+        Object[] args = methodInvocation.getArguments();
+        Map<Entity.EntityType, NameIdentifier> metadataContext =
+            extractNameIdentifierFromParameters(parameters, args);
+        AuthorizationExpressionEvaluator authorizationExpressionEvaluator =
+            new AuthorizationExpressionEvaluator(expression);
+        boolean authorizeResult = 
authorizationExpressionEvaluator.evaluate(metadataContext);
+        if (!authorizeResult) {
+          return Utils.internalError("Can not access metadata.");
+        }
+      }
+      return methodInvocation.proceed();
+    }
+
+    private Map<Entity.EntityType, NameIdentifier> 
extractNameIdentifierFromParameters(
+        Parameter[] parameters, Object[] args) {
+      Map<Entity.EntityType, String> metadatas = new HashMap<>();
+      Map<Entity.EntityType, NameIdentifier> nameIdentifierMap = new 
HashMap<>();
+      for (int i = 0; i < parameters.length; i++) {
+        Parameter parameter = parameters[i];
+        AuthorizationMetadata authorizeResource =
+            parameter.getAnnotation(AuthorizationMetadata.class);
+        if (authorizeResource == null) {
+          continue;
+        }
+        MetadataObject.Type type = authorizeResource.type();
+        metadatas.put(Entity.EntityType.valueOf(type.name()), 
String.valueOf(args[i]));
+      }
+      String metalake = metadatas.get(Entity.EntityType.METALAKE);
+      String catalog = metadatas.get(Entity.EntityType.CATALOG);
+      String schema = metadatas.get(Entity.EntityType.SCHEMA);
+      String table = metadatas.get(Entity.EntityType.TABLE);
+      String topic = metadatas.get(Entity.EntityType.TOPIC);
+      metadatas.forEach(
+          (type, metadata) -> {
+            switch (type) {
+              case CATALOG:
+                nameIdentifierMap.put(
+                    Entity.EntityType.CATALOG, 
NameIdentifierUtil.ofCatalog(metalake, catalog));
+                break;
+              case SCHEMA:
+                nameIdentifierMap.put(
+                    Entity.EntityType.SCHEMA,
+                    NameIdentifierUtil.ofSchema(metalake, catalog, schema));
+                break;
+              case TABLE:
+                nameIdentifierMap.put(
+                    Entity.EntityType.SCHEMA,
+                    NameIdentifierUtil.ofTable(metalake, catalog, schema, 
table));
+                break;
+              case TOPIC:
+                nameIdentifierMap.put(
+                    Entity.EntityType.SCHEMA,
+                    NameIdentifierUtil.ofTopic(metalake, catalog, schema, 
topic));
+                break;
+              case METALAKE:
+                nameIdentifierMap.put(
+                    Entity.EntityType.METALAKE, 
NameIdentifierUtil.ofMetalake(metalake));
+                break;
+              default:
+                break;
+            }
+          });
+      return nameIdentifierMap;
+    }
+  }
+
+  private static class ClassListFilter implements Filter {
+    private final Set<String> targetClasses;
+
+    public ClassListFilter(Set<String> targetClasses) {
+      this.targetClasses = new HashSet<>(targetClasses);
+    }
+
+    @Override
+    public boolean matches(Descriptor descriptor) {
+      String implementation = descriptor.getImplementation();
+      return targetClasses.contains(implementation);
     }
   }
 }
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 5a1ebcaded..9acd635f0c 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
@@ -38,6 +38,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.CatalogChange;
+import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.catalog.CatalogDispatcher;
@@ -52,6 +53,7 @@ import org.apache.gravitino.dto.responses.DropResponse;
 import org.apache.gravitino.dto.responses.EntityListResponse;
 import org.apache.gravitino.dto.util.DTOConverters;
 import org.apache.gravitino.metrics.MetricNames;
+import 
org.apache.gravitino.server.authorization.annotations.AuthorizationMetadata;
 import org.apache.gravitino.server.web.Utils;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.gravitino.utils.NamespaceUtil;
@@ -113,7 +115,9 @@ public class CatalogOperations {
   @Timed(name = "create-catalog." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
   @ResponseMetered(name = "create-catalog", absolute = true)
   public Response createCatalog(
-      @PathParam("metalake") String metalake, CatalogCreateRequest request) {
+      @PathParam("metalake") @AuthorizationMetadata(type = 
MetadataObject.Type.METALAKE)
+          String metalake,
+      CatalogCreateRequest request) {
     LOG.info("Received create catalog request for metalake: {}", metalake);
     try {
       return Utils.doAs(
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
new file mode 100644
index 0000000000..591935c8eb
--- /dev/null
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
@@ -0,0 +1,119 @@
+/*
+ * 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.web.filter;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.security.Principal;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.UserPrincipal;
+import org.apache.gravitino.authorization.Privilege;
+import org.apache.gravitino.dto.responses.ErrorResponse;
+import org.apache.gravitino.server.authorization.GravitinoAuthorizer;
+import org.apache.gravitino.server.authorization.GravitinoAuthorizerProvider;
+import 
org.apache.gravitino.server.authorization.annotations.AuthorizationExpression;
+import 
org.apache.gravitino.server.authorization.annotations.AuthorizationMetadata;
+import org.apache.gravitino.server.web.Utils;
+import org.apache.gravitino.utils.PrincipalUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+/** Test for {@link GravitinoInterceptionService}. */
+public class TestGravitinoInterceptionService {
+
+  @Test
+  public void testMetadataAuthorizationMethodInterceptor() throws Throwable {
+    try (MockedStatic<PrincipalUtils> principalUtilsMocked = 
mockStatic(PrincipalUtils.class);
+        MockedStatic<GravitinoAuthorizerProvider> mockStatic =
+            mockStatic(GravitinoAuthorizerProvider.class)) {
+      principalUtilsMocked
+          .when(PrincipalUtils::getCurrentPrincipal)
+          .thenReturn(new UserPrincipal("tester"));
+      MethodInvocation methodInvocation = mock(MethodInvocation.class);
+      GravitinoAuthorizerProvider mockedProvider = 
mock(GravitinoAuthorizerProvider.class);
+      
mockStatic.when(GravitinoAuthorizerProvider::getInstance).thenReturn(mockedProvider);
+      when(mockedProvider.getGravitinoAuthorizer()).thenReturn(new 
MockGravitinoAuthorizer());
+      GravitinoInterceptionService gravitinoInterceptionService =
+          new GravitinoInterceptionService();
+      Class<TestOperations> testOperationsClass = TestOperations.class;
+      Method[] methods = testOperationsClass.getMethods();
+      Method testMethod = methods[0];
+      List<MethodInterceptor> methodInterceptors =
+          gravitinoInterceptionService.getMethodInterceptors(testMethod);
+      MethodInterceptor methodInterceptor = methodInterceptors.get(0);
+      assertEquals(1, methodInterceptors.size());
+      when(methodInvocation.proceed()).thenReturn(new 
TestOperations().testMethod("testMetalake"));
+      when(methodInvocation.getMethod()).thenReturn(testMethod);
+      when(methodInvocation.getArguments()).thenReturn(new Object[] 
{"testMetalake"});
+      Response response = (Response) 
methodInterceptor.invoke(methodInvocation);
+      assertEquals("ok", response.getEntity());
+      when(methodInvocation.getMethod()).thenReturn(testMethod);
+      when(methodInvocation.getArguments()).thenReturn(new Object[] 
{"testMetalake2"});
+      Response response2 = (Response) 
methodInterceptor.invoke(methodInvocation);
+      assertEquals(
+          "Can not access metadata.", ((ErrorResponse) 
response2.getEntity()).getMessage());
+    }
+  }
+
+  public static class TestOperations {
+
+    @AuthorizationExpression(expression = "METALAKE::USE_CATALOG || 
METALAKE::OWNER")
+    public Response testMethod(
+        @AuthorizationMetadata(type = MetadataObject.Type.METALAKE) String 
metalake) {
+      return Utils.ok("ok");
+    }
+  }
+
+  private static class MockGravitinoAuthorizer implements GravitinoAuthorizer {
+
+    @Override
+    public void initialize() {}
+
+    @Override
+    public boolean authorize(
+        Principal principal,
+        String metalake,
+        MetadataObject metadataObject,
+        Privilege.Name privilege) {
+      return "tester".equals(principal.getName())
+          && "testMetalake".equals(metalake)
+          && metadataObject.type() == MetadataObject.Type.METALAKE
+          && privilege == Privilege.Name.USE_CATALOG;
+    }
+
+    @Override
+    public boolean isOwner(Principal principal, String metalake, 
MetadataObject metadataObject) {
+      return false;
+    }
+
+    @Override
+    public void handleRolePrivilegeChange(Long roleId) {}
+
+    @Override
+    public void close() throws IOException {}
+  }
+}

Reply via email to