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 becc173e412e031d3b79e252600ca600f245b1f2 Author: yangyang zhong <[email protected]> AuthorDate: Thu Jul 10 11:48:25 2025 +0800 [#7602] feat(authz): Support Model Authorization (#7605) ### What changes were proposed in this pull request? Support Model Authorization ### Why are the changes needed? close: #7602 ### Does this PR introduce _any_ user-facing change? None ### How was this patch tested? org.apache.gravitino.client.integration.test.authorization.ModelAuthorizationIT org.apache.gravitino.server.web.rest.authorization.TestModelAuthorizationExpression --- .../test/authorization/ModelAuthorizationIT.java | 316 +++++++++++++++ .../server/authorization/MetadataFilterHelper.java | 7 + .../AuthorizationExpressionConverter.java | 9 + .../web/filter/GravitinoInterceptionService.java | 10 +- .../server/web/rest/TestModelOperations.java | 3 +- .../TestModelAuthorizationExpression.java | 435 +++++++++++++++++++++ 6 files changed, 777 insertions(+), 3 deletions(-) diff --git a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/ModelAuthorizationIT.java b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/ModelAuthorizationIT.java new file mode 100644 index 0000000000..c66f417920 --- /dev/null +++ b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/ModelAuthorizationIT.java @@ -0,0 +1,316 @@ +/* + * 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.client.integration.test.authorization; + +import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.gravitino.Catalog; +import org.apache.gravitino.MetadataObject; +import org.apache.gravitino.MetadataObjects; +import org.apache.gravitino.NameIdentifier; +import org.apache.gravitino.Namespace; +import org.apache.gravitino.authorization.Privileges; +import org.apache.gravitino.authorization.SecurableObject; +import org.apache.gravitino.authorization.SecurableObjects; +import org.apache.gravitino.client.GravitinoMetalake; +import org.apache.gravitino.model.Model; +import org.apache.gravitino.model.ModelCatalog; +import org.apache.gravitino.model.ModelChange; +import org.apache.gravitino.model.ModelVersion; +import org.apache.gravitino.model.ModelVersionChange; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +@Tag("gravitino-docker-test") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class ModelAuthorizationIT extends BaseRestApiAuthorizationIT { + + private static final String CATALOG = "catalog"; + + private static final String SCHEMA = "SCHEMA"; + + private static String role = "role"; + + @BeforeAll + public void startIntegrationTest() throws Exception { + super.startIntegrationTest(); + client + .loadMetalake(METALAKE) + .createCatalog(CATALOG, Catalog.Type.MODEL, "model", "comment", new HashMap<>()) + .asSchemas() + .createSchema(SCHEMA, "test", new HashMap<>()); + // try to load the schema as normal user, expect failure + assertThrows( + "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.", + RuntimeException.class, + () -> { + normalUserClient + .loadMetalake(METALAKE) + .loadCatalog(CATALOG) + .asSchemas() + .loadSchema(SCHEMA); + }); + // grant tester privilege + List<SecurableObject> securableObjects = new ArrayList<>(); + GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE); + SecurableObject catalogObject = + SecurableObjects.ofCatalog(CATALOG, ImmutableList.of(Privileges.UseCatalog.allow())); + securableObjects.add(catalogObject); + gravitinoMetalake.createRole(role, new HashMap<>(), securableObjects); + gravitinoMetalake.grantRolesToUser(ImmutableList.of(role), NORMAL_USER); + // normal user can load the catalog but not the schema + Catalog catalogLoadByNormalUser = normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + assertEquals(CATALOG, catalogLoadByNormalUser.name()); + assertThrows( + "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.", + RuntimeException.class, + () -> { + catalogLoadByNormalUser.asSchemas().loadSchema(SCHEMA); + }); + } + + @Test + @Order(1) + public void testCreateModel() { + ModelCatalog modelCatalog = client.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + modelCatalog.registerModel(NameIdentifier.of(SCHEMA, "model1"), "", new HashMap<>()); + ModelCatalog normalUserCatalog = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "}.", + RuntimeException.class, + () -> { + normalUserCatalog.registerModel(NameIdentifier.of(SCHEMA, "model2"), "", new HashMap<>()); + }); + GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE); + // test grant create schema privilege + gravitinoMetalake.grantPrivilegesToRole( + role, + MetadataObjects.of(null, CATALOG, MetadataObject.Type.CATALOG), + ImmutableList.of(Privileges.UseSchema.allow(), Privileges.CreateModel.allow())); + normalUserCatalog.registerModel(NameIdentifier.of(SCHEMA, "model2"), "", new HashMap<>()); + normalUserCatalog.registerModel(NameIdentifier.of(SCHEMA, "model3"), "", new HashMap<>()); + } + + @Test + @Order(2) + public void testListModel() { + NameIdentifier[] modelsLoadByNormalUser = + normalUserClient + .loadMetalake(METALAKE) + .loadCatalog(CATALOG) + .asModelCatalog() + .listModels(Namespace.of(SCHEMA)); + assertEquals(2, modelsLoadByNormalUser.length); + List<String> modelNamesLoadByNormalUser = + Arrays.stream(modelsLoadByNormalUser) + .map(model -> model.name()) + .collect(Collectors.toList()); + assertLinesMatch(modelNamesLoadByNormalUser, ImmutableList.of("model2", "model3")); + NameIdentifier[] models = + client + .loadMetalake(METALAKE) + .loadCatalog(CATALOG) + .asModelCatalog() + .listModels(Namespace.of(SCHEMA)); + assertEquals(3, models.length); + List<String> modelNames = + Arrays.stream(models).map(model -> model.name()).collect(Collectors.toList()); + assertLinesMatch(modelNames, ImmutableList.of("model1", "model2", "model3")); + } + + @Test + @Order(3) + public void testLoadModel() { + Catalog catalogEntityLoadByNormalUser = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + ModelCatalog modelCatalog = catalogEntityLoadByNormalUser.asModelCatalog(); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "}.", + RuntimeException.class, + () -> { + modelCatalog.getModel(NameIdentifier.of(SCHEMA, "model1")); + }); + // test grant use model + GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE); + gravitinoMetalake.grantPrivilegesToRole( + role, + MetadataObjects.of(CATALOG, SCHEMA, MetadataObject.Type.SCHEMA), + ImmutableList.of(Privileges.UseSchema.allow(), Privileges.UseModel.allow())); + Model modelEntity = modelCatalog.getModel(NameIdentifier.of(SCHEMA, "model1")); + assertEquals("model1", modelEntity.name()); + // reset privilege + gravitinoMetalake.revokePrivilegesFromRole( + role, + MetadataObjects.of(CATALOG, SCHEMA, MetadataObject.Type.SCHEMA), + ImmutableSet.of(Privileges.UseModel.allow())); + } + + @Test + @Order(4) + public void testAlterModel() { + Catalog catalogEntityLoadByNormalUser = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + ModelCatalog modelCatalogLoadByNormalUser = catalogEntityLoadByNormalUser.asModelCatalog(); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "}.", + RuntimeException.class, + () -> { + modelCatalogLoadByNormalUser.alterModel( + NameIdentifier.of(SCHEMA, "model1"), new ModelChange.RenameModel("model5")); + }); + ModelCatalog modelCatalog = client.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + modelCatalog.alterModel( + NameIdentifier.of(SCHEMA, "model1"), new ModelChange.RenameModel("model4")); + modelCatalog.alterModel( + NameIdentifier.of(SCHEMA, "model4"), new ModelChange.RenameModel("model1")); + } + + @Test + @Order(5) + public void testDropModel() { + ModelCatalog modelCatalog = client.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + modelCatalog.registerModel(NameIdentifier.of(SCHEMA, "model5"), "", new HashMap<>()); + Catalog catalogEntityLoadByNormalUser = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + ModelCatalog modelCatalogLoadByNormalUser = catalogEntityLoadByNormalUser.asModelCatalog(); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "}.", + RuntimeException.class, + () -> { + modelCatalogLoadByNormalUser.deleteModel(NameIdentifier.of(SCHEMA, "model5")); + }); + modelCatalog.deleteModel(NameIdentifier.of(SCHEMA, "model5")); + } + + @Test + @Order(6) + public void testLinkModel() { + ModelCatalog modelCatalog = client.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + Catalog catalogEntityLoadByNormalUser = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + ModelCatalog modelCatalogLoadByNormalUser = catalogEntityLoadByNormalUser.asModelCatalog(); + modelCatalog.linkModelVersion( + NameIdentifier.of(SCHEMA, "model1"), "uri1", new String[] {"alias1"}, "comment2", null); + modelCatalog.linkModelVersion( + NameIdentifier.of(SCHEMA, "model1"), "uri2", new String[] {"alias2"}, "comment2", null); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "model1" + "}.", + RuntimeException.class, + () -> { + modelCatalogLoadByNormalUser.linkModelVersion( + NameIdentifier.of(SCHEMA, "model1"), + "uri1", + new String[] {"alias2"}, + "comment2", + null); + }); + } + + @Test + @Order(7) + public void testListModelVersion() { + ModelCatalog modelCatalog = client.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + Catalog catalogEntityLoadByNormalUser = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + ModelCatalog modelCatalogLoadByNormalUser = catalogEntityLoadByNormalUser.asModelCatalog(); + int[] versions = modelCatalog.listModelVersions(NameIdentifier.of(SCHEMA, "model1")); + assertEquals(2, versions.length); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "model1" + "}.", + RuntimeException.class, + () -> { + modelCatalogLoadByNormalUser.linkModelVersion( + NameIdentifier.of(SCHEMA, "model1"), + "uri1", + new String[] {"alias2"}, + "comment2", + null); + }); + } + + @Test + @Order(8) + public void testLoadModelVersion() { + ModelCatalog modelCatalog = client.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + Catalog catalogEntityLoadByNormalUser = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + ModelCatalog modelCatalogLoadByNormalUser = catalogEntityLoadByNormalUser.asModelCatalog(); + ModelVersion version = modelCatalog.getModelVersion(NameIdentifier.of(SCHEMA, "model1"), 1); + assertEquals(1, version.version()); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "model1" + "}.", + RuntimeException.class, + () -> { + modelCatalogLoadByNormalUser.getModelVersion(NameIdentifier.of(SCHEMA, "model1"), 1); + }); + } + + @Test + @Order(9) + public void testAlterModelVersion() { + ModelCatalog modelCatalog = client.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + Catalog catalogEntityLoadByNormalUser = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + ModelCatalog modelCatalogLoadByNormalUser = catalogEntityLoadByNormalUser.asModelCatalog(); + ModelVersion version = + modelCatalog.alterModelVersion( + NameIdentifier.of(SCHEMA, "model1"), 1, ModelVersionChange.setProperty("key", "value")); + assertEquals("value", version.properties().get("key")); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "model1" + "}.", + RuntimeException.class, + () -> { + modelCatalogLoadByNormalUser.alterModelVersion( + NameIdentifier.of(SCHEMA, "model1"), + 1, + ModelVersionChange.setProperty("key", "value")); + }); + } + + @Test + @Order(10) + public void testDropModelVersion() { + ModelCatalog modelCatalog = client.loadMetalake(METALAKE).loadCatalog(CATALOG).asModelCatalog(); + Catalog catalogEntityLoadByNormalUser = + normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG); + ModelCatalog modelCatalogLoadByNormalUser = catalogEntityLoadByNormalUser.asModelCatalog(); + assertThrows( + "Can not access metadata {" + METALAKE + "," + CATALOG + "." + SCHEMA + "model1" + "}.", + RuntimeException.class, + () -> { + modelCatalogLoadByNormalUser.deleteModelVersion(NameIdentifier.of(SCHEMA, "model1"), 1); + }); + modelCatalog.deleteModelVersion(NameIdentifier.of(SCHEMA, "model1"), 1); + int[] versions = modelCatalog.listModelVersions(NameIdentifier.of(SCHEMA, "model1")); + assertEquals(1, versions.length); + } +} 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 9d508b84b2..13fed7c5a6 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 @@ -121,6 +121,13 @@ public class MetadataFilterHelper { nameIdentifierMap.put( Entity.EntityType.CATALOG, NameIdentifierUtil.getCatalogIdentifier(nameIdentifier)); break; + case MODEL: + nameIdentifierMap.put(Entity.EntityType.MODEL, nameIdentifier); + nameIdentifierMap.put( + Entity.EntityType.SCHEMA, NameIdentifierUtil.getSchemaIdentifier(nameIdentifier)); + nameIdentifierMap.put( + Entity.EntityType.CATALOG, NameIdentifierUtil.getCatalogIdentifier(nameIdentifier)); + break; default: break; } diff --git a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java index bc4c2d5d67..f21ef43824 100644 --- a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java +++ b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConverter.java @@ -145,6 +145,15 @@ public class AuthorizationExpressionConverter { expression.replaceAll( "SCHEMA_OWNER_WITH_USE_CATALOG", "SCHEMA::OWNER && (ANY(USE_CATALOG, METALAKE, CATALOG))"); + expression = + expression.replaceAll( + "ANY_USE_MODEL", "(ANY(USE_MODEL, METALAKE, CATALOG, SCHEMA, MODEL))"); + expression = + expression.replaceAll( + "ANY_CREATE_MODEL_VERSION", + "(ANY(CREATE_MODEL_VERSION, METALAKE, CATALOG, SCHEMA, MODEL))"); + expression = + expression.replaceAll("ANY_CREATE_MODEL", "(ANY(CREATE_MODEL, METALAKE, CATALOG, SCHEMA))"); return expression; } } 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 c25743eb5a..9f799d2776 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 @@ -40,6 +40,7 @@ import org.apache.gravitino.server.authorization.annotations.AuthorizationMetada 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.server.web.rest.ModelOperations; import org.apache.gravitino.server.web.rest.SchemaOperations; import org.apache.gravitino.server.web.rest.TableOperations; import org.apache.gravitino.utils.NameIdentifierUtil; @@ -60,7 +61,8 @@ public class GravitinoInterceptionService implements InterceptionService { ImmutableSet.of( CatalogOperations.class.getName(), SchemaOperations.class.getName(), - TableOperations.class.getName())); + TableOperations.class.getName(), + ModelOperations.class.getName())); } @Override @@ -162,6 +164,12 @@ public class GravitinoInterceptionService implements InterceptionService { nameIdentifierMap.put( Entity.EntityType.METALAKE, NameIdentifierUtil.ofMetalake(metalake)); break; + case MODEL: + String model = metadatas.get(Entity.EntityType.MODEL); + nameIdentifierMap.put( + Entity.EntityType.MODEL, + NameIdentifierUtil.ofModel(metadata, catalog, schema, model)); + break; default: break; } diff --git a/server/src/test/java/org/apache/gravitino/server/web/rest/TestModelOperations.java b/server/src/test/java/org/apache/gravitino/server/web/rest/TestModelOperations.java index d720ecab72..3068444dc8 100644 --- a/server/src/test/java/org/apache/gravitino/server/web/rest/TestModelOperations.java +++ b/server/src/test/java/org/apache/gravitino/server/web/rest/TestModelOperations.java @@ -65,12 +65,11 @@ import org.apache.gravitino.utils.NameIdentifierUtil; import org.apache.gravitino.utils.NamespaceUtil; import org.glassfish.jersey.internal.inject.AbstractBinder; import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.test.JerseyTest; import org.glassfish.jersey.test.TestProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class TestModelOperations extends JerseyTest { +public class TestModelOperations extends BaseOperationsTest { private static class MockServletRequestFactory extends ServletRequestFactoryBase { diff --git a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestModelAuthorizationExpression.java b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestModelAuthorizationExpression.java new file mode 100644 index 0000000000..b2a97ffe7e --- /dev/null +++ b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestModelAuthorizationExpression.java @@ -0,0 +1,435 @@ +/* + * 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.rest.authorization; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.google.common.collect.ImmutableSet; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import ognl.OgnlException; +import org.apache.gravitino.dto.requests.ModelRegisterRequest; +import org.apache.gravitino.dto.requests.ModelUpdatesRequest; +import org.apache.gravitino.dto.requests.ModelVersionLinkRequest; +import org.apache.gravitino.dto.requests.ModelVersionUpdatesRequest; +import org.apache.gravitino.server.authorization.annotations.AuthorizationExpression; +import org.apache.gravitino.server.web.rest.ModelOperations; +import org.junit.jupiter.api.Test; + +public class TestModelAuthorizationExpression { + + @Test + public void testCreateModel() throws NoSuchMethodException, OgnlException { + Method method = + ModelOperations.class.getMethod( + "registerModel", String.class, String.class, String.class, ModelRegisterRequest.class); + AuthorizationExpression authorizationExpressionAnnotation = + method.getAnnotation(AuthorizationExpression.class); + String expression = authorizationExpressionAnnotation.expression(); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(expression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "CATALOG::CREATE_MODEL", "CATALOG::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "METALAKE::CREATE_MODEL", "METALAKE::USE_SCHEMA", "METALAKE::USE_CATALOG"))); + } + + @Test + public void testLoadModel() throws OgnlException, NoSuchFieldException, IllegalAccessException { + Field loadTableAuthorizationExpressionField = + ModelOperations.class.getDeclaredField("loadModelAuthorizationExpression"); + loadTableAuthorizationExpressionField.setAccessible(true); + String loadTableAuthExpression = (String) loadTableAuthorizationExpressionField.get(null); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(loadTableAuthExpression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::USE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::USE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("CATALOG::USE_MODEL", "CATALOG::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "METALAKE::USE_MODEL", "METALAKE::USE_SCHEMA", "METALAKE::USE_CATALOG"))); + } + + @Test + public void testAlterModel() throws NoSuchMethodException, OgnlException { + Method method = + ModelOperations.class.getMethod( + "alterModel", + String.class, + String.class, + String.class, + String.class, + ModelUpdatesRequest.class); + AuthorizationExpression authorizationExpressionAnnotation = + method.getAnnotation(AuthorizationExpression.class); + String expression = authorizationExpressionAnnotation.expression(); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(expression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + } + + @Test + public void testDropModel() throws NoSuchMethodException, OgnlException { + Method method = + ModelOperations.class.getMethod( + "deleteModel", String.class, String.class, String.class, String.class); + AuthorizationExpression authorizationExpressionAnnotation = + method.getAnnotation(AuthorizationExpression.class); + String expression = authorizationExpressionAnnotation.expression(); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(expression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + } + + @Test + public void testLinkModelVersion() throws NoSuchMethodException, OgnlException { + Method method = + ModelOperations.class.getMethod( + "linkModelVersion", + String.class, + String.class, + String.class, + String.class, + ModelVersionLinkRequest.class); + AuthorizationExpression authorizationExpressionAnnotation = + method.getAnnotation(AuthorizationExpression.class); + String expression = authorizationExpressionAnnotation.expression(); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(expression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::CREATE_MODEL_VERSION"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of("MODEL::CREATE_MODEL_VERSION", "SCHEMA::USE_SCHEMA"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::CREATE_MODEL_VERSION", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::CREATE_MODEL_VERSION", + "MODEL::USE_MODEL", + "SCHEMA::USE_SCHEMA", + "CATALOG::USE_CATALOG"))); + + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "SCHEMA::CREATE_MODEL_VERSION", + "MODEL::USE_MODEL", + "SCHEMA::USE_SCHEMA", + "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "CATALOG::CREATE_MODEL_VERSION", + "MODEL::USE_MODEL", + "CATALOG::USE_SCHEMA", + "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "METALAKE::CREATE_MODEL_VERSION", + "MODEL::USE_MODEL", + "METALAKE::USE_SCHEMA", + "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", "CATALOG::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "CATALOG::CREATE_MODEL_VERSION", + "MODEL::USE_MODEL", + "CATALOG::USE_SCHEMA", + "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "METALAKE::CREATE_MODEL_VERSION", + "MODEL::USE_MODEL", + "METALAKE::USE_SCHEMA", + "METALAKE::USE_CATALOG"))); + } + + @Test + public void testAlterModelVersion() throws NoSuchMethodException, OgnlException { + Method method = + ModelOperations.class.getMethod( + "alterModelVersion", + String.class, + String.class, + String.class, + String.class, + int.class, + ModelVersionUpdatesRequest.class); + AuthorizationExpression authorizationExpressionAnnotation = + method.getAnnotation(AuthorizationExpression.class); + String expression = authorizationExpressionAnnotation.expression(); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(expression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + } + + @Test + public void testAlterModelVersionByAlias() throws NoSuchMethodException, OgnlException { + Method method = + ModelOperations.class.getMethod( + "alterModelVersionByAlias", + String.class, + String.class, + String.class, + String.class, + String.class, + ModelVersionUpdatesRequest.class); + AuthorizationExpression authorizationExpressionAnnotation = + method.getAnnotation(AuthorizationExpression.class); + String expression = authorizationExpressionAnnotation.expression(); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(expression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + } + + @Test + public void testDropModelVersion() throws NoSuchMethodException, OgnlException { + Method method = + ModelOperations.class.getMethod( + "deleteModelVersion", + String.class, + String.class, + String.class, + String.class, + int.class); + AuthorizationExpression authorizationExpressionAnnotation = + method.getAnnotation(AuthorizationExpression.class); + String expression = authorizationExpressionAnnotation.expression(); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(expression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + } + + @Test + public void testDropModelVersionByAlias() throws NoSuchMethodException, OgnlException { + Method method = + ModelOperations.class.getMethod( + "deleteModelVersionByAlias", + String.class, + String.class, + String.class, + String.class, + String.class); + AuthorizationExpression authorizationExpressionAnnotation = + method.getAnnotation(AuthorizationExpression.class); + String expression = authorizationExpressionAnnotation.expression(); + MockAuthorizationExpressionEvaluator mockEvaluator = + new MockAuthorizationExpressionEvaluator(expression); + assertFalse(mockEvaluator.getResult(ImmutableSet.of())); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "CATALOG::USE_CATALOG"))); + assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER", "METALAKE::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL"))); + assertFalse( + mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA"))); + assertFalse( + mockEvaluator.getResult( + ImmutableSet.of("SCHEMA::CREATE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER"))); + assertFalse(mockEvaluator.getResult(ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of("MODEL::OWNER", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + assertTrue( + mockEvaluator.getResult( + ImmutableSet.of( + "MODEL::OWNER", "MODEL::USE_MODEL", "SCHEMA::USE_SCHEMA", "CATALOG::USE_CATALOG"))); + } +}
