This is an automated email from the ASF dual-hosted git repository.

jshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git

commit f4ac64bcb35d4cd7c38bfd99e9cc37aeca3a5578
Author: [email protected] <[email protected]>
AuthorDate: Tue Jul 15 15:31:23 2025 +0800

    [#6762] feat(authz): Support Metadata Authorization
---
 .../java/org/apache/gravitino/GravitinoEnv.java    |  20 ++
 .../gravitino/authorization/OwnerManager.java      |   2 +-
 gradle/libs.versions.toml                          |   9 +-
 server-common/build.gradle.kts                     |   5 +-
 .../server/authorization/MetadataIdConverter.java  |   5 +-
 .../apache/gravitino/server/GravitinoServer.java   |   7 +-
 .../server/web/rest/FilesetOperations.java         | 102 +++++++---
 .../gravitino/server/web/rest/ModelOperations.java | 215 ++++++++++++++++-----
 8 files changed, 291 insertions(+), 74 deletions(-)

diff --git a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java 
b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
index e8b9f06ed3..ccb75f1764 100644
--- a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
+++ b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
@@ -23,6 +23,7 @@ import org.apache.gravitino.audit.AuditLogManager;
 import org.apache.gravitino.authorization.AccessControlDispatcher;
 import org.apache.gravitino.authorization.AccessControlManager;
 import org.apache.gravitino.authorization.FutureGrantManager;
+import org.apache.gravitino.authorization.GravitinoAuthorizer;
 import org.apache.gravitino.authorization.OwnerDispatcher;
 import org.apache.gravitino.authorization.OwnerEventManager;
 import org.apache.gravitino.authorization.OwnerManager;
@@ -136,6 +137,7 @@ public class GravitinoEnv {
   private EventBus eventBus;
   private OwnerDispatcher ownerDispatcher;
   private FutureGrantManager futureGrantManager;
+  private GravitinoAuthorizer gravitinoAuthorizer;
 
   protected GravitinoEnv() {}
 
@@ -366,6 +368,24 @@ public class GravitinoEnv {
     return eventListenerManager;
   }
 
+  /**
+   * Set GravitinoAuthorizer to GravitinoEnv
+   *
+   * @param gravitinoAuthorizer the GravitinoAuthorizer instance
+   */
+  public void setGravitinoAuthorizer(GravitinoAuthorizer gravitinoAuthorizer) {
+    this.gravitinoAuthorizer = gravitinoAuthorizer;
+  }
+
+  /**
+   * Get The GravitinoAuthorizer
+   *
+   * @return the GravitinoAuthorizer instance
+   */
+  public GravitinoAuthorizer gravitinoAuthorizer() {
+    return gravitinoAuthorizer;
+  }
+
   public void start() {
     metricsSystem.start();
     eventListenerManager.start();
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/OwnerManager.java 
b/core/src/main/java/org/apache/gravitino/authorization/OwnerManager.java
index 8e22b0e433..c6e1c0315f 100644
--- a/core/src/main/java/org/apache/gravitino/authorization/OwnerManager.java
+++ b/core/src/main/java/org/apache/gravitino/authorization/OwnerManager.java
@@ -132,7 +132,6 @@ public class OwnerManager implements OwnerDispatcher {
     }
   }
 
-  @Override
   private void notifyOwnerChange(Owner oldOwner, String metalake, 
MetadataObject metadataObject) {
     GravitinoAuthorizer gravitinoAuthorizer = 
GravitinoEnv.getInstance().gravitinoAuthorizer();
     if (gravitinoAuthorizer != null) {
@@ -160,6 +159,7 @@ public class OwnerManager implements OwnerDispatcher {
     }
   }
 
+  @Override
   public Optional<Owner> getOwner(String metalake, MetadataObject 
metadataObject) {
     NameIdentifier ident = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
     OwnerImpl owner = new OwnerImpl();
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 8330ca8759..4745cf87b2 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -119,10 +119,12 @@ hudi = "0.15.0"
 google-auth = "1.28.0"
 aliyun-credentials = "0.3.12"
 openlineage = "1.29.0"
-concurrent-trees = "2.6.0"
 jcstress = "0.8.15"
 jmh-plugin = "0.7.3"
 jmh = "1.37"
+jcasbin = "1.81.0"
+ognl = "3.4.7"
+concurrent-trees = "2.6.0"
 
 [libraries]
 aws-iam = { group = "software.amazon.awssdk", name = "iam", version.ref = 
"awssdk" }
@@ -284,9 +286,10 @@ google-auth-credentials = { group = "com.google.auth", 
name = "google-auth-libra
 
 aliyun-credentials-sdk = { group='com.aliyun', name='credentials-java', 
version.ref='aliyun-credentials' }
 flinkjdbc = {group='org.apache.flink',name='flink-connector-jdbc', 
version.ref='flinkjdbc'}
-openlineage-java= { group = "io.openlineage", name = "openlineage-java", 
version.ref = "openlineage" }
-
 concurrent-trees = { group = "com.googlecode.concurrent-trees", name = 
"concurrent-trees", version.ref = "concurrent-trees" }
+jcasbin = { group='org.casbin', name='jcasbin', version.ref="jcasbin" }
+openlineage-java= { group = "io.openlineage", name = "openlineage-java", 
version.ref = "openlineage" }
+ognl = { group='ognl', name='ognl', version.ref="ognl" }
 
 [bundles]
 log4j = ["slf4j-api", "log4j-slf4j2-impl", "log4j-api", "log4j-core", 
"log4j-12-api", "log4j-layout-template-json"]
diff --git a/server-common/build.gradle.kts b/server-common/build.gradle.kts
index f8d27a0446..943ff3758e 100644
--- a/server-common/build.gradle.kts
+++ b/server-common/build.gradle.kts
@@ -43,8 +43,11 @@ dependencies {
   implementation(libs.jackson.datatype.jdk8)
   implementation(libs.jackson.datatype.jsr310)
   implementation(libs.jackson.databind)
-  implementation(libs.prometheus.servlet)
+  implementation(libs.jcasbin) {
+    exclude(group = "com.fasterxml.jackson.core", module = "jackson-databind")
+  }
   implementation(libs.ognl)
+  implementation(libs.prometheus.servlet)
 
   testImplementation(libs.commons.io)
   testImplementation(libs.junit.jupiter.api)
diff --git 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataIdConverter.java
 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataIdConverter.java
index fc1155d926..e7deaa15b0 100644
--- 
a/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataIdConverter.java
+++ 
b/server-common/src/main/java/org/apache/gravitino/server/authorization/MetadataIdConverter.java
@@ -97,9 +97,8 @@ public class MetadataIdConverter {
    * @param metadataObject The metadata object to convert.
    * @param metalake The metalake name.
    * @return The metadata id.
-   * @throws IOException if an error occurs while loading the entity.
    */
-  public static Long getID(MetadataObject metadataObject, String metalake) 
throws IOException {
+  public static Long getID(MetadataObject metadataObject, String metalake) {
     Preconditions.checkArgument(metadataObject != null, "Metadata object 
cannot be null");
     EntityStore entityStore = GravitinoEnv.getInstance().entityStore();
     CatalogManager catalogManager = 
GravitinoEnv.getInstance().catalogManager();
@@ -119,7 +118,7 @@ public class MetadataIdConverter {
     try {
       entity = entityStore.get(normalizedIdent, entityType, 
getEntityClass(entityType));
     } catch (IOException e) {
-      throw new IOException(
+      throw new RuntimeException(
           "failed to load entity from entity store: " + 
metadataObject.fullName(), e);
     }
 
diff --git 
a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java 
b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
index 582e0d105a..4262446fe6 100644
--- a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
+++ b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
@@ -19,6 +19,7 @@
 package org.apache.gravitino.server;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.HashSet;
 import java.util.Properties;
 import javax.inject.Singleton;
@@ -40,6 +41,7 @@ import org.apache.gravitino.metalake.MetalakeDispatcher;
 import org.apache.gravitino.metrics.MetricsSystem;
 import org.apache.gravitino.metrics.source.MetricsSource;
 import org.apache.gravitino.server.authentication.ServerAuthenticator;
+import org.apache.gravitino.server.authorization.GravitinoAuthorizerProvider;
 import org.apache.gravitino.server.web.ConfigServlet;
 import org.apache.gravitino.server.web.HttpServerMetricsSource;
 import org.apache.gravitino.server.web.JettyServer;
@@ -98,6 +100,8 @@ public class GravitinoServer extends ResourceConfig {
 
     ServerAuthenticator.getInstance().initialize(serverConfig);
 
+    GravitinoAuthorizerProvider.getInstance().initialize(serverConfig);
+
     lineageService.initialize(
         new 
LineageConfig(serverConfig.getConfigsWithPrefix(LineageConfig.LINEAGE_CONFIG_PREFIX)));
 
@@ -177,7 +181,8 @@ public class GravitinoServer extends ResourceConfig {
     server.join();
   }
 
-  public void stop() {
+  public void stop() throws IOException {
+    GravitinoAuthorizerProvider.getInstance().close();
     server.stop();
     gravitinoEnv.shutdown();
     if (lineageService != null) {
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/FilesetOperations.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/FilesetOperations.java
index 2d4eb8ead7..94a4b03bde 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/FilesetOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/FilesetOperations.java
@@ -45,6 +45,8 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.audit.CallerContext;
@@ -63,6 +65,9 @@ import org.apache.gravitino.file.Fileset;
 import org.apache.gravitino.file.FilesetChange;
 import org.apache.gravitino.metrics.MetricNames;
 import org.apache.gravitino.rest.RESTUtils;
+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;
@@ -76,6 +81,11 @@ public class FilesetOperations {
 
   private final FilesetDispatcher dispatcher;
 
+  private static final String loadFilesetAuthorizationExpression =
+      "ANY(OWNER, METALAKE, CATALOG) || "
+          + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+          + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (FILESET::OWNER || 
ANY_READ_FILESET || ANY_WRITE_FILESET)";
+
   @Context private HttpServletRequest httpRequest;
 
   @Inject
@@ -88,9 +98,13 @@ public class FilesetOperations {
   @Timed(name = "list-fileset." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "list-fileset", absolute = true)
   public Response listFilesets(
-      @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) {
+
     try {
       LOG.info("Received list filesets request for schema: {}.{}.{}", 
metalake, catalog, schema);
       return Utils.doAs(
@@ -98,6 +112,12 @@ public class FilesetOperations {
           () -> {
             Namespace filesetNS = NamespaceUtil.ofFileset(metalake, catalog, 
schema);
             NameIdentifier[] idents = dispatcher.listFilesets(filesetNS);
+            idents =
+                MetadataFilterHelper.filterByExpression(
+                    metalake,
+                    loadFilesetAuthorizationExpression,
+                    Entity.EntityType.FILESET,
+                    idents);
             Response response = Utils.ok(new EntityListResponse(idents));
             LOG.info(
                 "List {} filesets under schema: {}.{}.{}",
@@ -117,10 +137,18 @@ public class FilesetOperations {
   @Produces("application/vnd.gravitino.v1+json")
   @Timed(name = "create-fileset." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
   @ResponseMetered(name = "create-fileset", absolute = true)
+  @AuthorizationExpression(
+      expression =
+          "ANY(OWNER, METALAKE, CATALOG) || "
+              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && SCHEMA::CREATE_FILESET",
+      accessMetadataType = MetadataObject.Type.FILESET)
   public Response createFileset(
-      @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,
       FilesetCreateRequest request) {
     LOG.info(
         "Received create fileset request: {}.{}.{}.{}",
@@ -168,11 +196,17 @@ public class FilesetOperations {
   @Produces("application/vnd.gravitino.v1+json")
   @Timed(name = "load-fileset." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "load-fileset", absolute = true)
+  @AuthorizationExpression(
+      expression = loadFilesetAuthorizationExpression,
+      accessMetadataType = MetadataObject.Type.FILESET)
   public Response loadFileset(
-      @PathParam("metalake") String metalake,
-      @PathParam("catalog") String catalog,
-      @PathParam("schema") String schema,
-      @PathParam("fileset") String fileset) {
+      @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("fileset") @AuthorizationMetadata(type = 
MetadataObject.Type.FILESET)
+          String fileset) {
     LOG.info("Received load fileset request: {}.{}.{}.{}", metalake, catalog, 
schema, fileset);
     try {
       return Utils.doAs(
@@ -244,11 +278,20 @@ public class FilesetOperations {
   @Produces("application/vnd.gravitino.v1+json")
   @Timed(name = "alter-fileset." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "alter-fileset", absolute = true)
+  @AuthorizationExpression(
+      expression =
+          "ANY(OWNER, METALAKE, CATALOG) || "
+              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (FILESET::OWNER || 
ANY_WRITE_FILESET)",
+      accessMetadataType = MetadataObject.Type.FILESET)
   public Response alterFileset(
-      @PathParam("metalake") String metalake,
-      @PathParam("catalog") String catalog,
-      @PathParam("schema") String schema,
-      @PathParam("fileset") String fileset,
+      @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("fileset") @AuthorizationMetadata(type = 
MetadataObject.Type.FILESET)
+          String fileset,
       FilesetUpdatesRequest request) {
     LOG.info("Received alter fileset request: {}.{}.{}.{}", metalake, catalog, 
schema, fileset);
     try {
@@ -277,11 +320,20 @@ public class FilesetOperations {
   @Produces("application/vnd.gravitino.v1+json")
   @Timed(name = "drop-fileset." + MetricNames.HTTP_PROCESS_DURATION, absolute 
= true)
   @ResponseMetered(name = "drop-fileset", absolute = true)
+  @AuthorizationExpression(
+      expression =
+          "ANY(OWNER, METALAKE, CATALOG) || "
+              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
+              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && FILESET::OWNER",
+      accessMetadataType = MetadataObject.Type.FILESET)
   public Response dropFileset(
-      @PathParam("metalake") String metalake,
-      @PathParam("catalog") String catalog,
-      @PathParam("schema") String schema,
-      @PathParam("fileset") String fileset) {
+      @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("fileset") @AuthorizationMetadata(type = 
MetadataObject.Type.FILESET)
+          String fileset) {
     LOG.info("Received drop fileset request: {}.{}.{}.{}", metalake, catalog, 
schema, fileset);
     try {
       return Utils.doAs(
@@ -308,11 +360,17 @@ public class FilesetOperations {
   @Produces("application/vnd.gravitino.v1+json")
   @Timed(name = "get-file-location." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
   @ResponseMetered(name = "get-file-location", absolute = true)
+  @AuthorizationExpression(
+      expression = loadFilesetAuthorizationExpression,
+      accessMetadataType = MetadataObject.Type.FILESET)
   public Response getFileLocation(
-      @PathParam("metalake") String metalake,
-      @PathParam("catalog") String catalog,
-      @PathParam("schema") String schema,
-      @PathParam("fileset") String fileset,
+      @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("fileset") @AuthorizationMetadata(type = 
MetadataObject.Type.FILESET)
+          String fileset,
       @QueryParam("sub_path") @NotNull String subPath,
       @QueryParam("location_name") String locationName) {
     LOG.info(
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 {


Reply via email to