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 {