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 {} + } +}
