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 e692269cd4 [#6787] feat(authz): Introduce GravitinoInterceptionService
(#7258)
e692269cd4 is described below
commit e692269cd42b8c170bb147a61ea440c37e3c772f
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 85a7a0f3d9..cbdcc67bd0 100644
--- a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
+++ b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
@@ -22,6 +22,7 @@ import java.io.File;
import java.io.IOException;
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;
@@ -49,11 +50,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;
@@ -123,6 +126,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 {}
+ }
+}