This is an automated email from the ASF dual-hosted git repository.
roryqi 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 32a578abbe [#7602] feat(authz): Support Model Authorization (#7605)
32a578abbe is described below
commit 32a578abbe8bd8137662ff625960014ef44802d2
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 +-
.../gravitino/server/web/rest/ModelOperations.java | 215 ++++++++--
.../server/web/rest/TestModelOperations.java | 3 +-
.../TestModelAuthorizationExpression.java | 435 +++++++++++++++++++++
7 files changed, 949 insertions(+), 46 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/main/java/org/apache/gravitino/server/web/rest/ModelOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/ModelOperations.java
index 455165ec47..b5c5ff7e66 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/ModelOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/ModelOperations.java
@@ -20,6 +20,7 @@ package org.apache.gravitino.server.web.rest;
import com.codahale.metrics.annotation.ResponseMetered;
import com.codahale.metrics.annotation.Timed;
+import java.util.Arrays;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DELETE;
@@ -33,6 +34,8 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.Namespace;
import org.apache.gravitino.catalog.ModelDispatcher;
@@ -55,6 +58,9 @@ import org.apache.gravitino.model.Model;
import org.apache.gravitino.model.ModelChange;
import org.apache.gravitino.model.ModelVersion;
import org.apache.gravitino.model.ModelVersionChange;
+import org.apache.gravitino.server.authorization.MetadataFilterHelper;
+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.NameIdentifierUtil;
import org.apache.gravitino.utils.NamespaceUtil;
@@ -66,6 +72,11 @@ public class ModelOperations {
private static final Logger LOG =
LoggerFactory.getLogger(ModelOperations.class);
+ private static final String loadModelAuthorizationExpression =
+ "ANY(OWNER, METALAKE, CATALOG) ||"
+ + " SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + " ANY_USE_CATALOG && ANY_USE_SCHEMA && (MODEL::OWNER ||
ANY_USE_MODEL)";
+
private final ModelDispatcher modelDispatcher;
@Context private HttpServletRequest httpRequest;
@@ -92,6 +103,9 @@ public class ModelOperations {
() -> {
NameIdentifier[] modelIds = modelDispatcher.listModels(modelNs);
modelIds = modelIds == null ? new NameIdentifier[0] : modelIds;
+ modelIds =
+ MetadataFilterHelper.filterByExpression(
+ metalake, loadModelAuthorizationExpression,
Entity.EntityType.MODEL, modelIds);
LOG.info("List {} models under schema {}", modelIds.length,
modelNs);
return Utils.ok(new EntityListResponse(modelIds));
});
@@ -106,11 +120,16 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "get-model." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "get-model", absolute = true)
+ @AuthorizationExpression(
+ expression = loadModelAuthorizationExpression,
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response getModel(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model) {
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model) {
LOG.info("Received get model request: {}.{}.{}.{}", metalake, catalog,
schema, model);
NameIdentifier modelId = NameIdentifierUtil.ofModel(metalake, catalog,
schema, model);
@@ -132,10 +151,18 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "register-model." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "register-model", absolute = true)
+ @AuthorizationExpression(
+ expression =
+ " ANY(OWNER, METALAKE, CATALOG) || "
+ + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + "ANY_USE_CATALOG && ANY_USE_SCHEMA && ANY_CREATE_MODEL",
+ accessMetadataType = MetadataObject.Type.SCHEMA)
public Response registerModel(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
ModelRegisterRequest request) {
LOG.info(
"Received register model request: {}.{}.{}.{}",
@@ -170,11 +197,19 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "delete-model." + MetricNames.HTTP_PROCESS_DURATION, absolute
= true)
@ResponseMetered(name = "delete-model", absolute = true)
+ @AuthorizationExpression(
+ expression =
+ " ANY(OWNER, METALAKE, CATALOG) || "
+ + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response deleteModel(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model) {
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model) {
LOG.info("Received delete model request: {}.{}.{}.{}", metalake, catalog,
schema, model);
NameIdentifier modelId = NameIdentifierUtil.ofModel(metalake, catalog,
schema, model);
@@ -218,12 +253,48 @@ public class ModelOperations {
if (verbose) {
ModelVersion[] modelVersions =
modelDispatcher.listModelVersionInfos(modelId);
modelVersions = modelVersions == null ? new ModelVersion[0] :
modelVersions;
+ modelVersions =
+ Arrays.stream(modelVersions)
+ .filter(
+ modelVersion -> {
+ NameIdentifier[] nameIdentifiers =
+ new NameIdentifier[] {
+ NameIdentifierUtil.ofModelVersion(
+ metalake, catalog, schema, model,
modelVersion.version())
+ };
+ return MetadataFilterHelper.filterByExpression(
+ metalake,
+ loadModelAuthorizationExpression,
+ Entity.EntityType.MODEL_VERSION,
+ nameIdentifiers)
+ .length
+ > 0;
+ })
+ .toArray(ModelVersion[]::new);
LOG.info("List {} versions of model {}", modelVersions.length,
modelId);
return Utils.ok(
new
ModelVersionInfoListResponse(DTOConverters.toDTOs(modelVersions)));
} else {
int[] versions = modelDispatcher.listModelVersions(modelId);
versions = versions == null ? new int[0] : versions;
+ versions =
+ Arrays.stream(versions)
+ .filter(
+ modelVersion -> {
+ NameIdentifier[] nameIdentifiers =
+ new NameIdentifier[] {
+ NameIdentifierUtil.ofModelVersion(
+ metalake, catalog, schema, model,
modelVersion)
+ };
+ return MetadataFilterHelper.filterByExpression(
+ metalake,
+ loadModelAuthorizationExpression,
+ Entity.EntityType.MODEL_VERSION,
+ nameIdentifiers)
+ .length
+ > 0;
+ })
+ .toArray();
LOG.info("List {} versions of model {}", versions.length,
modelId);
return Utils.ok(new ModelVersionListResponse(versions));
}
@@ -239,11 +310,16 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "get-model-version." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "get-model-version", absolute = true)
+ @AuthorizationExpression(
+ expression = loadModelAuthorizationExpression,
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response getModelVersion(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model,
@PathParam("version") int version) {
LOG.info(
"Received get model version request: {}.{}.{}.{}.{}",
@@ -274,11 +350,16 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "get-model-alias." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "get-model-alias", absolute = true)
+ @AuthorizationExpression(
+ expression = loadModelAuthorizationExpression,
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response getModelVersionByAlias(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model,
@PathParam("alias") String alias) {
LOG.info(
"Received get model version alias request: {}.{}.{}.{}.{}",
@@ -309,11 +390,19 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "link-model-version." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "link-model-version", absolute = true)
+ @AuthorizationExpression(
+ expression =
+ " ANY(OWNER, METALAKE, CATALOG) || "
+ + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (MODEL::OWNER ||
ANY_USE_MODEL && ANY_CREATE_MODEL_VERSION)",
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response linkModelVersion(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model,
ModelVersionLinkRequest request) {
LOG.info("Received link model version request: {}.{}.{}.{}", metalake,
catalog, schema, model);
NameIdentifier modelId = NameIdentifierUtil.ofModel(metalake, catalog,
schema, model);
@@ -344,11 +433,19 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "delete-model-version." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "delete-model-version", absolute = true)
+ @AuthorizationExpression(
+ expression =
+ " ANY(OWNER, METALAKE, CATALOG) || "
+ + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response deleteModelVersion(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model,
@PathParam("version") int version) {
LOG.info(
"Received delete model version request: {}.{}.{}.{}.{}",
@@ -384,11 +481,19 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "delete-model-alias." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "delete-model-alias", absolute = true)
+ @AuthorizationExpression(
+ expression =
+ " ANY(OWNER, METALAKE, CATALOG) || "
+ + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response deleteModelVersionByAlias(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model,
@PathParam("alias") String alias) {
LOG.info(
"Received delete model version by alias request: {}.{}.{}.{}.{}",
@@ -425,11 +530,19 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "alter-model-version." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "alter-model-version", absolute = true)
+ @AuthorizationExpression(
+ expression =
+ " ANY(OWNER, METALAKE, CATALOG) || "
+ + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response alterModelVersion(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model,
@PathParam("version") int version,
ModelVersionUpdatesRequest request) {
LOG.info(
@@ -471,11 +584,19 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "alter-model-alias." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "alter-model-alias", absolute = true)
+ @AuthorizationExpression(
+ expression =
+ " ANY(OWNER, METALAKE, CATALOG) || "
+ + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response alterModelVersionByAlias(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model,
@PathParam("alias") String alias,
ModelVersionUpdatesRequest request) {
LOG.info(
@@ -516,11 +637,19 @@ public class ModelOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "alter-model." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "alter-model", absolute = true)
+ @AuthorizationExpression(
+ expression =
+ " ANY(OWNER, METALAKE, CATALOG) || "
+ + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+ + "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
+ accessMetadataType = MetadataObject.Type.MODEL)
public Response alterModel(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("model") String model,
+ @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
MetadataObject.Type.MODEL) String model,
ModelUpdatesRequest request) {
LOG.info("Received alter model request: {}.{}.{}.{}", metalake, catalog,
schema, model);
try {
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")));
+ }
+}