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")));
+  }
+}

Reply via email to