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

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


The following commit(s) were added to refs/heads/main by this push:
     new 5d8fd86f20 [#8266] feat(authz): support list file Authorization (#8294)
5d8fd86f20 is described below

commit 5d8fd86f20643f04f075cafe543292da9a278415
Author: yangyang zhong <[email protected]>
AuthorDate: Wed Aug 27 20:32:12 2025 +0800

    [#8266] feat(authz): support list file Authorization (#8294)
    
    ### What changes were proposed in this pull request?
    
    support list file Authorization
    
    ### Why are the changes needed?
    
    Fix: #8266
    
    ### Does this PR introduce _any_ user-facing change?
    
    None
    
    ### How was this patch tested?
    
    
    
org.apache.gravitino.client.integration.test.authorization.FilesetAuthorizationIT
---
 .../test/authorization/FilesetAuthorizationIT.java | 109 +++++++++++++++++++++
 .../server/web/rest/FilesetOperations.java         |  12 ++-
 2 files changed, 117 insertions(+), 4 deletions(-)

diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/FilesetAuthorizationIT.java
 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/FilesetAuthorizationIT.java
index bbffa30902..8e99f86fba 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/FilesetAuthorizationIT.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/FilesetAuthorizationIT.java
@@ -24,14 +24,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.List;
+import org.apache.commons.lang3.StringUtils;
 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.auth.AuthConstants;
 import org.apache.gravitino.authorization.Owner;
 import org.apache.gravitino.authorization.Privileges;
 import org.apache.gravitino.authorization.SecurableObject;
@@ -44,6 +50,13 @@ import org.apache.gravitino.file.FilesetChange;
 import org.apache.gravitino.integration.test.container.ContainerSuite;
 import org.apache.gravitino.integration.test.container.HiveContainer;
 import org.apache.gravitino.integration.test.util.GravitinoITUtils;
+import org.apache.http.HttpHeaders;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.MethodOrderer;
 import org.junit.jupiter.api.Order;
@@ -62,6 +75,7 @@ public class FilesetAuthorizationIT extends 
BaseRestApiAuthorizationIT {
 
   private String role = "role";
   private String defaultBaseLocation;
+  protected CloseableHttpClient httpClient;
 
   @BeforeAll
   public void startIntegrationTest() throws Exception {
@@ -101,6 +115,15 @@ public class FilesetAuthorizationIT extends 
BaseRestApiAuthorizationIT {
         () -> {
           catalogLoadByNormalUser.asSchemas().loadSchema(SCHEMA);
         });
+
+    httpClient = HttpClients.createDefault();
+  }
+
+  @AfterAll
+  public void closeHttpClient() throws IOException {
+    if (httpClient != null) {
+      httpClient.close();
+    }
   }
 
   @Test
@@ -260,10 +283,50 @@ public class FilesetAuthorizationIT extends 
BaseRestApiAuthorizationIT {
         NORMAL_USER,
         Owner.Type.USER);
     filesetCatalogNormalUser.getFileLocation(NameIdentifier.of(SCHEMA, 
"fileset1"), "/test");
+    // reset
+    gravitinoMetalake.setOwner(
+        MetadataObjects.of(
+            ImmutableList.of(CATALOG, SCHEMA, "fileset1"), 
MetadataObject.Type.FILESET),
+        USER,
+        Owner.Type.USER);
   }
 
   @Test
   @Order(6)
+  public void testListFiles() throws IOException {
+    GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+    FilesetCatalog filesetCatalogNormalUser =
+        
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+    // reset privilege
+    gravitinoMetalake.revokePrivilegesFromRole(
+        role,
+        MetadataObjects.of(
+            ImmutableList.of(CATALOG, SCHEMA, "fileset1"), 
MetadataObject.Type.FILESET),
+        ImmutableSet.of(Privileges.WriteFileset.allow(), 
Privileges.ReadFileset.allow()));
+
+    // normal user cannot alter fileset1 (no privilege)
+    assertThrows(
+        String.format("Can not access metadata {%s.%s.%s}.", CATALOG, SCHEMA, 
"fileset1"),
+        ForbiddenException.class,
+        () -> getFileInfos("fileset1", "/test", "", NORMAL_USER));
+    // grant normal user owner privilege on fileset4
+    gravitinoMetalake.setOwner(
+        MetadataObjects.of(
+            ImmutableList.of(CATALOG, SCHEMA, "fileset1"), 
MetadataObject.Type.FILESET),
+        NORMAL_USER,
+        Owner.Type.USER);
+    filesetCatalogNormalUser.getFileLocation(NameIdentifier.of(SCHEMA, 
"fileset1"), "/test");
+    getFileInfos("fileset1", "/test", "", NORMAL_USER);
+    // reset
+    gravitinoMetalake.setOwner(
+        MetadataObjects.of(
+            ImmutableList.of(CATALOG, SCHEMA, "fileset1"), 
MetadataObject.Type.FILESET),
+        USER,
+        Owner.Type.USER);
+  }
+
+  @Test
+  @Order(7)
   public void testDropFileset() {
     GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
     FilesetCatalog filesetCatalogNormalUser =
@@ -312,4 +375,50 @@ public class FilesetAuthorizationIT extends 
BaseRestApiAuthorizationIT {
   private String storageLocation(String filesetName) {
     return defaultBaseLocation() + "/" + filesetName;
   }
+
+  private void getFileInfos(String fileset, String subPath, String 
locationName, String user)
+      throws IOException {
+    String targetPath =
+        "/api/metalakes/"
+            + METALAKE
+            + "/catalogs/"
+            + CATALOG
+            + "/schemas/"
+            + SCHEMA
+            + "/filesets/"
+            + fileset
+            + "/files";
+
+    URIBuilder uriBuilder;
+    try {
+      uriBuilder = new URIBuilder(serverUri + targetPath);
+    } catch (URISyntaxException e) {
+      throw new IOException("Error constructing URI: " + serverUri + 
targetPath, e);
+    }
+
+    if (!StringUtils.isBlank(subPath)) {
+      uriBuilder.addParameter("subPath", subPath);
+    }
+    if (!StringUtils.isBlank(locationName)) {
+      uriBuilder.addParameter("locationName", locationName);
+    }
+
+    HttpGet httpGet;
+    try {
+      httpGet = new HttpGet(uriBuilder.build());
+      String authorization =
+          (AuthConstants.AUTHORIZATION_BASIC_HEADER
+              + new String(
+                  
Base64.getEncoder().encode(user.getBytes(StandardCharsets.UTF_8)),
+                  StandardCharsets.UTF_8));
+      httpGet.addHeader(HttpHeaders.AUTHORIZATION, authorization);
+    } catch (URISyntaxException e) {
+      throw new IOException("Failed to build URI with query parameters: " + 
uriBuilder, e);
+    }
+    try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) {
+      if (httpResponse.getStatusLine().getStatusCode() == 403) {
+        throw new ForbiddenException("Can not get files");
+      }
+    }
+  }
 }
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 bc06646bad..55a152dc70 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
@@ -224,11 +224,15 @@ public class FilesetOperations {
   @Produces("application/vnd.gravitino.v1+json")
   @Timed(name = "list-fileset-files." + MetricNames.HTTP_PROCESS_DURATION, 
absolute = true)
   @ResponseMetered(name = "list-fileset-files", absolute = true)
+  @AuthorizationExpression(
+      expression = loadFilesetAuthorizationExpression,
+      accessMetadataType = MetadataObject.Type.FILESET)
   public Response listFiles(
-      @PathParam("metalake") String metalake,
-      @PathParam("catalog") String catalog,
-      @PathParam("schema") String schema,
-      @PathParam("fileset") String fileset,
+      @PathParam("metalake") @AuthorizationMetadata(type = 
Entity.EntityType.METALAKE)
+          String metalake,
+      @PathParam("catalog") @AuthorizationMetadata(type = 
Entity.EntityType.CATALOG) String catalog,
+      @PathParam("schema") @AuthorizationMetadata(type = 
Entity.EntityType.SCHEMA) String schema,
+      @PathParam("fileset") @AuthorizationMetadata(type = 
Entity.EntityType.FILESET) String fileset,
       @QueryParam("sub_path") @DefaultValue("/") String subPath,
       @QueryParam("location_name") String locationName)
       throws UnsupportedEncodingException {

Reply via email to