This is an automated email from the ASF dual-hosted git repository.
liuxun 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 e49354f043 [#7545] feat(authz): Support fileset authorization (#7581)
e49354f043 is described below
commit e49354f0438ba7707a13ba5e355c3440634d3f1e
Author: Eric Chang <[email protected]>
AuthorDate: Mon Jul 14 10:42:50 2025 +0800
[#7545] feat(authz): Support fileset authorization (#7581)
<!--
1. Title: [#<issue>] <type>(<scope>): <subject>
Examples:
- "[#123] feat(operator): support xxx"
- "[#233] fix: check null before access result in xxx"
- "[MINOR] refactor: fix typo in variable name"
- "[MINOR] docs: fix typo in README"
- "[#255] test: fix flaky test NameOfTheTest"
Reference: https://www.conventionalcommits.org/en/v1.0.0/
2. If the PR is unfinished, please mark this PR as draft.
-->
### What changes were proposed in this pull request?
Support fileset auth with annotation.
### Why are the changes needed?
To support fileset auth.
Fix: #7545
### Does this PR introduce _any_ user-facing change?
Yes.
### How was this patch tested?
FilesetAuthorizationIT
---------
Co-authored-by: yangyang zhong <[email protected]>
Co-authored-by: [email protected] <[email protected]>
---
.../test/authorization/FilesetAuthorizationIT.java | 308 +++++++++++++++++++++
.../server/authorization/MetadataFilterHelper.java | 7 +
.../AuthorizationExpressionConverter.java | 6 +
.../web/filter/GravitinoInterceptionService.java | 14 +-
.../server/web/rest/FilesetOperations.java | 102 +++++--
.../server/web/rest/TestFilesetOperations.java | 3 +-
.../TestFilesetAuthorizationExpression.java | 186 +++++++++++++
.../TestModelAuthorizationExpression.java | 8 +-
8 files changed, 603 insertions(+), 31 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
new file mode 100644
index 0000000000..f43e03b540
--- /dev/null
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/FilesetAuthorizationIT.java
@@ -0,0 +1,308 @@
+/*
+ * 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.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+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.Owner;
+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.file.Fileset;
+import org.apache.gravitino.file.FilesetCatalog;
+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.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 FilesetAuthorizationIT extends BaseRestApiAuthorizationIT {
+
+ private static final String CATALOG = "catalog";
+ private static final String SCHEMA = "schema";
+
+ private static final ContainerSuite containerSuite =
ContainerSuite.getInstance();
+
+ private String role = "role";
+ private String defaultBaseLocation;
+
+ @BeforeAll
+ public void startIntegrationTest() throws Exception {
+ containerSuite.startHiveContainer();
+ super.startIntegrationTest();
+ client
+ .loadMetalake(METALAKE)
+ .createCatalog(
+ CATALOG, Catalog.Type.FILESET, "hadoop", "comment",
ImmutableMap.of("k1", "k2"))
+ .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 testCreateFileset() {
+ // admin user can create fileset
+ FilesetCatalog filesetCatalog =
+ client.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+ String filename1 =
GravitinoITUtils.genRandomName("FilesetAuthorizationIT_fileset1");
+ filesetCatalog.createFileset(
+ // NameIdentifier.of(SCHEMA, "fileset1"),
+ NameIdentifier.of(SCHEMA, "fileset1"),
+ "comment",
+ Fileset.Type.MANAGED,
+ storageLocation(filename1),
+ new HashMap<>());
+ // normal use cannot create fileset
+ FilesetCatalog filesetCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+ assertThrows(
+ "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.",
+ RuntimeException.class,
+ () -> {
+ filesetCatalogNormalUser.createFileset(
+ // NameIdentifier.of(SCHEMA, "fileset2"),
+ NameIdentifier.of(SCHEMA, "fileset2"),
+ "comment",
+ Fileset.Type.MANAGED,
+
storageLocation(GravitinoITUtils.genRandomName("FilesetAuthorizationIT_fileset2")),
+ new HashMap<>());
+ });
+ // grant privileges
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ gravitinoMetalake.grantPrivilegesToRole(
+ role,
+ MetadataObjects.of(CATALOG, SCHEMA, MetadataObject.Type.SCHEMA),
+ ImmutableList.of(Privileges.UseSchema.allow(),
Privileges.CreateFileset.allow()));
+ // normal user can now create fileset
+ String filename2 =
GravitinoITUtils.genRandomName("FilesetAuthorizationIT_fileset2");
+ filesetCatalogNormalUser.createFileset(
+ NameIdentifier.of(SCHEMA, "fileset2"),
+ "comment",
+ Fileset.Type.MANAGED,
+ storageLocation(filename2),
+ new HashMap<>());
+ String filename3 =
GravitinoITUtils.genRandomName("FilesetAuthorizationIT_fileset3");
+ filesetCatalogNormalUser.createFileset(
+ NameIdentifier.of(SCHEMA, "fileset3"),
+ "comment",
+ Fileset.Type.MANAGED,
+ storageLocation(filename3),
+ new HashMap<>());
+ }
+
+ @Test
+ @Order(2)
+ public void testListFileset() {
+ FilesetCatalog tableCatalog =
+ client.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+ NameIdentifier[] tablesList =
tableCatalog.listFilesets(Namespace.of(SCHEMA));
+ assertArrayEquals(
+ new NameIdentifier[] {
+ NameIdentifier.of(SCHEMA, "fileset1"),
+ NameIdentifier.of(SCHEMA, "fileset2"),
+ NameIdentifier.of(SCHEMA, "fileset3"),
+ },
+ tablesList);
+ // normal user can only see filesets which they have privilege for
+ FilesetCatalog tableCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+ NameIdentifier[] filesetsListNormalUser =
+ tableCatalogNormalUser.listFilesets(Namespace.of(SCHEMA));
+ assertArrayEquals(
+ new NameIdentifier[] {
+ NameIdentifier.of(SCHEMA, "fileset2"), NameIdentifier.of(SCHEMA,
"fileset3")
+ },
+ filesetsListNormalUser);
+ }
+
+ @Test
+ @Order(3)
+ public void testLoadFileset() {
+ FilesetCatalog filesetCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+ // normal user can load fileset2 and fileset3, but not fileset1
+ assertThrows(
+ String.format("Can not access metadata {%s.%s.%s}.", CATALOG, SCHEMA,
"fileset1"),
+ RuntimeException.class,
+ () -> {
+ filesetCatalogNormalUser.loadFileset(NameIdentifier.of(CATALOG,
SCHEMA, "fileset1"));
+ });
+ Fileset fileset2 =
filesetCatalogNormalUser.loadFileset(NameIdentifier.of(SCHEMA, "fileset2"));
+ assertEquals("fileset2", fileset2.name());
+ Fileset fileset3 =
filesetCatalogNormalUser.loadFileset(NameIdentifier.of(SCHEMA, "fileset3"));
+ assertEquals("fileset3", fileset3.name());
+
+ // grant normal user privilege to use fileset1
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ gravitinoMetalake.grantPrivilegesToRole(
+ role,
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "fileset1"),
MetadataObject.Type.FILESET),
+ ImmutableList.of(Privileges.ReadFileset.allow()));
+ Fileset fileset1 =
filesetCatalogNormalUser.loadFileset(NameIdentifier.of(SCHEMA, "fileset1"));
+ assertEquals("fileset1", fileset1.name());
+ }
+
+ @Test
+ @Order(4)
+ public void testAlterFileset() {
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ FilesetCatalog filesetCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+
+ // normal user cannot alter fileset1 (no privilege)
+ assertThrows(
+ String.format("Can not access metadata {%s.%s.%s}.", CATALOG, SCHEMA,
"fileset1"),
+ RuntimeException.class,
+ () -> {
+ filesetCatalogNormalUser.alterFileset(
+ NameIdentifier.of(SCHEMA, "fileset1"),
FilesetChange.setProperty("key", "value"));
+ });
+ // grant normal user write privilege on fileset1
+ gravitinoMetalake.grantPrivilegesToRole(
+ role,
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "fileset1"),
MetadataObject.Type.FILESET),
+ ImmutableList.of(Privileges.WriteFileset.allow()));
+ filesetCatalogNormalUser.alterFileset(
+ NameIdentifier.of(SCHEMA, "fileset1"),
FilesetChange.setProperty("key", "value"));
+ }
+
+ @Test
+ @Order(5)
+ public void testGetFileLocation() {
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ FilesetCatalog filesetCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+
+ // normal user cannot alter fileset1 (no privilege)
+ assertThrows(
+ String.format("Can not access metadata {%s.%s.%s}.", CATALOG, SCHEMA,
"fileset1"),
+ RuntimeException.class,
+ () -> {
+ filesetCatalogNormalUser.getFileLocation(
+ NameIdentifier.of(METALAKE, CATALOG, SCHEMA, "fileset1"),
"/test");
+ });
+ // 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");
+ }
+
+ @Test
+ @Order(6)
+ public void testDropFileset() {
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ FilesetCatalog filesetCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+ // reset owner
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "fileset1"),
MetadataObject.Type.FILESET),
+ USER,
+ Owner.Type.USER);
+ // normal user cannot drop fileset1
+ assertThrows(
+ String.format("Can not access metadata {%s.%s.%s}.", CATALOG, SCHEMA,
"fileset1"),
+ RuntimeException.class,
+ () -> {
+ filesetCatalogNormalUser.dropFileset(NameIdentifier.of(SCHEMA,
"fileset1"));
+ });
+ // normal user can drop fileset2 and fileset3 (they created them)
+ filesetCatalogNormalUser.dropFileset(NameIdentifier.of(SCHEMA,
"fileset2"));
+ filesetCatalogNormalUser.dropFileset(NameIdentifier.of(SCHEMA,
"fileset3"));
+
+ // owner can drop fileset1
+ FilesetCatalog filesetCatalog =
+ client.loadMetalake(METALAKE).loadCatalog(CATALOG).asFilesetCatalog();
+ filesetCatalog.dropFileset(NameIdentifier.of(SCHEMA, "fileset1"));
+ // check filesets are dropped
+ NameIdentifier[] filesetsList =
filesetCatalog.listFilesets(Namespace.of(SCHEMA));
+ assertArrayEquals(new NameIdentifier[] {}, filesetsList);
+ NameIdentifier[] filesetsListNormalUser =
+ filesetCatalogNormalUser.listFilesets(Namespace.of(SCHEMA));
+ assertArrayEquals(new NameIdentifier[] {}, filesetsListNormalUser);
+ }
+
+ private String defaultBaseLocation() {
+ if (defaultBaseLocation == null) {
+ defaultBaseLocation =
+ String.format(
+ "hdfs://%s:%d/user/hive/%s",
+ containerSuite.getHiveContainer().getContainerIpAddress(),
+ HiveContainer.HDFS_DEFAULTFS_PORT,
+ SCHEMA.toLowerCase());
+ }
+ return defaultBaseLocation;
+ }
+
+ private String storageLocation(String filesetName) {
+ return defaultBaseLocation() + "/" + filesetName;
+ }
+}
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 2655b98a05..066c550c40 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
@@ -144,6 +144,13 @@ public class MetadataFilterHelper {
nameIdentifierMap.put(
Entity.EntityType.CATALOG,
NameIdentifierUtil.getCatalogIdentifier(nameIdentifier));
break;
+ case FILESET:
+ nameIdentifierMap.put(Entity.EntityType.FILESET, 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 0ec8ef18a1..694dafbf6d 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
@@ -163,6 +163,12 @@ public class AuthorizationExpressionConverter {
expression =
expression.replaceAll(
"ANY_CONSUME_TOPIC", "(ANY(CONSUME_TOPIC, METALAKE, CATALOG,
SCHEMA, TOPIC))");
+ expression =
+ expression.replaceAll(
+ "ANY_READ_FILESET", "(ANY(READ_FILESET, METALAKE, CATALOG, SCHEMA,
FILESET))");
+ expression =
+ expression.replaceAll(
+ "ANY_WRITE_FILESET", "(ANY(WRITE_FILESET, METALAKE, CATALOG,
SCHEMA, FILESET))");
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 d39faf0ad8..7d26113aaf 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.FilesetOperations;
import org.apache.gravitino.server.web.rest.ModelOperations;
import org.apache.gravitino.server.web.rest.SchemaOperations;
import org.apache.gravitino.server.web.rest.TableOperations;
@@ -64,7 +65,8 @@ public class GravitinoInterceptionService implements
InterceptionService {
SchemaOperations.class.getName(),
TableOperations.class.getName(),
ModelOperations.class.getName(),
- TopicOperations.class.getName()));
+ TopicOperations.class.getName(),
+ FilesetOperations.class.getName()));
}
@Override
@@ -140,6 +142,7 @@ public class GravitinoInterceptionService implements
InterceptionService {
String schema = metadatas.get(Entity.EntityType.SCHEMA);
String table = metadatas.get(Entity.EntityType.TABLE);
String topic = metadatas.get(Entity.EntityType.TOPIC);
+ String fileset = metadatas.get(Entity.EntityType.FILESET);
metadatas.forEach(
(type, metadata) -> {
switch (type) {
@@ -162,9 +165,10 @@ public class GravitinoInterceptionService implements
InterceptionService {
Entity.EntityType.TOPIC,
NameIdentifierUtil.ofTopic(metalake, catalog, schema,
topic));
break;
- case METALAKE:
+ case FILESET:
nameIdentifierMap.put(
- Entity.EntityType.METALAKE,
NameIdentifierUtil.ofMetalake(metalake));
+ Entity.EntityType.FILESET,
+ NameIdentifierUtil.ofFileset(metalake, catalog, schema,
fileset));
break;
case MODEL:
String model = metadatas.get(Entity.EntityType.MODEL);
@@ -172,6 +176,10 @@ public class GravitinoInterceptionService implements
InterceptionService {
Entity.EntityType.MODEL,
NameIdentifierUtil.ofModel(metadata, catalog, schema,
model));
break;
+ case METALAKE:
+ nameIdentifierMap.put(
+ Entity.EntityType.METALAKE,
NameIdentifierUtil.ofMetalake(metalake));
+ break;
default:
break;
}
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/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java
index f8248bc1d8..56665ae914 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestFilesetOperations.java
@@ -69,7 +69,6 @@ import org.apache.gravitino.lock.LockManager;
import org.apache.gravitino.rest.RESTUtils;
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.BeforeAll;
@@ -77,7 +76,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.mockito.stubbing.Answer;
-public class TestFilesetOperations extends JerseyTest {
+public class TestFilesetOperations extends BaseOperationsTest {
private static class MockServletRequestFactory extends
ServletRequestFactoryBase {
@Override
public HttpServletRequest get() {
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestFilesetAuthorizationExpression.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestFilesetAuthorizationExpression.java
new file mode 100644
index 0000000000..74f8fd45e5
--- /dev/null
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/authorization/TestFilesetAuthorizationExpression.java
@@ -0,0 +1,186 @@
+/*
+ * 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.FilesetCreateRequest;
+import org.apache.gravitino.dto.requests.FilesetUpdatesRequest;
+import
org.apache.gravitino.server.authorization.annotations.AuthorizationExpression;
+import org.apache.gravitino.server.web.rest.FilesetOperations;
+import org.junit.jupiter.api.Test;
+
+public class TestFilesetAuthorizationExpression {
+
+ @Test
+ public void testCreateFileset() throws NoSuchMethodException, OgnlException {
+ Method method =
+ FilesetOperations.class.getMethod(
+ "createFileset", String.class, String.class, String.class,
FilesetCreateRequest.class);
+ AuthorizationExpression authorizationExpressionAnnotation =
+ method.getAnnotation(AuthorizationExpression.class);
+ String expression = authorizationExpressionAnnotation.expression();
+ MockAuthorizationExpressionEvaluator mockEvaluator =
+ new MockAuthorizationExpressionEvaluator(expression);
+ assertFalse(mockEvaluator.getResult(ImmutableSet.of()));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_CATALOG")));
+ 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")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_METALAKE")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::CREATE_CATALOG")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_FILESET")));
+ assertFalse(
+ mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_FILESET",
"CATALOG::USE_CATALOG")));
+ assertTrue(
+ mockEvaluator.getResult(
+ ImmutableSet.of(
+ "SCHEMA::CREATE_FILESET", "SCHEMA::USE_SCHEMA",
"CATALOG::USE_CATALOG")));
+ }
+
+ @Test
+ public void testLoadFileset() throws OgnlException, NoSuchFieldException,
IllegalAccessException {
+ Field loadFilesetAuthorizationExpressionField =
+
FilesetOperations.class.getDeclaredField("loadFilesetAuthorizationExpression");
+ loadFilesetAuthorizationExpressionField.setAccessible(true);
+ String loadFilesetAuthorizationExpression =
+ (String) loadFilesetAuthorizationExpressionField.get(null);
+ MockAuthorizationExpressionEvaluator mockEvaluator =
+ new
MockAuthorizationExpressionEvaluator(loadFilesetAuthorizationExpression);
+ assertFalse(mockEvaluator.getResult(ImmutableSet.of()));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_CATALOG")));
+ assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_METALAKE")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::CREATE_CATALOG")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_FILESET")));
+ assertFalse(
+ mockEvaluator.getResult(
+ ImmutableSet.of("SCHEMA::CREATE_FILESET",
"CATALOG::CREATE_CATALOG")));
+
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::WRITE_FILESET")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::WRITE_FILESET")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::WRITE_FILESET")));
+ assertFalse(
+ mockEvaluator.getResult(ImmutableSet.of("SCHEMA::WRITE_FILESET",
"CATALOG::USE_CATALOG")));
+ assertTrue(
+ mockEvaluator.getResult(
+ ImmutableSet.of(
+ "SCHEMA::WRITE_FILESET", "SCHEMA::USE_SCHEMA",
"CATALOG::USE_CATALOG")));
+
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::READ_FILESET")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::READ_FILESET")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::READ_FILESET")));
+ assertFalse(
+ mockEvaluator.getResult(ImmutableSet.of("SCHEMA::READ_FILESET",
"CATALOG::USE_CATALOG")));
+ assertTrue(
+ mockEvaluator.getResult(
+ ImmutableSet.of("SCHEMA::READ_FILESET", "SCHEMA::USE_SCHEMA",
"CATALOG::USE_CATALOG")));
+
+ assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
+ assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER",
"CATALOG::USE_CATALOG")));
+ assertTrue(
+ mockEvaluator.getResult(
+ ImmutableSet.of("SCHEMA::OWNER", "SCHEMA::USE_SCHEMA",
"CATALOG::USE_CATALOG")));
+ }
+
+ @Test
+ public void testAlterFileset() throws NoSuchMethodException, OgnlException {
+ Method method =
+ FilesetOperations.class.getMethod(
+ "alterFileset",
+ String.class,
+ String.class,
+ String.class,
+ String.class,
+ FilesetUpdatesRequest.class);
+ AuthorizationExpression authorizationExpressionAnnotation =
+ method.getAnnotation(AuthorizationExpression.class);
+ String expression = authorizationExpressionAnnotation.expression();
+ MockAuthorizationExpressionEvaluator mockEvaluator =
+ new MockAuthorizationExpressionEvaluator(expression);
+ assertFalse(mockEvaluator.getResult(ImmutableSet.of()));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_CATALOG")));
+ assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_METALAKE")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::CREATE_CATALOG")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_FILESET")));
+ assertFalse(
+ mockEvaluator.getResult(
+ ImmutableSet.of("SCHEMA::CREATE_FILESET",
"CATALOG::CREATE_CATALOG")));
+
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::WRITE_FILESET")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::WRITE_FILESET")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::WRITE_FILESET")));
+ assertFalse(
+ mockEvaluator.getResult(ImmutableSet.of("SCHEMA::WRITE_FILESET",
"CATALOG::USE_CATALOG")));
+ assertTrue(
+ mockEvaluator.getResult(
+ ImmutableSet.of(
+ "SCHEMA::WRITE_FILESET", "SCHEMA::USE_SCHEMA",
"CATALOG::USE_CATALOG")));
+
+ assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
+ assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER",
"CATALOG::USE_CATALOG")));
+ assertTrue(
+ mockEvaluator.getResult(
+ ImmutableSet.of("SCHEMA::OWNER", "SCHEMA::USE_SCHEMA",
"CATALOG::USE_CATALOG")));
+ }
+
+ @Test
+ public void testDropFileset() throws NoSuchMethodException, OgnlException {
+ Method method =
+ FilesetOperations.class.getMethod(
+ "dropFileset", 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()));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_CATALOG")));
+ assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER")));
+
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::USE_METALAKE")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::CREATE_CATALOG")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::CREATE_FILESET")));
+ assertFalse(
+ mockEvaluator.getResult(
+ ImmutableSet.of("SCHEMA::CREATE_FILESET",
"CATALOG::CREATE_CATALOG")));
+
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("METALAKE::WRITE_FILESET")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("CATALOG::WRITE_FILESET")));
+
assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::WRITE_FILESET")));
+ assertFalse(
+ mockEvaluator.getResult(ImmutableSet.of("SCHEMA::WRITE_FILESET",
"CATALOG::USE_CATALOG")));
+ assertFalse(
+ mockEvaluator.getResult(
+ ImmutableSet.of(
+ "SCHEMA::WRITE_FILESET", "SCHEMA::USE_SCHEMA",
"CATALOG::USE_CATALOG")));
+
+ assertFalse(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER")));
+ assertTrue(mockEvaluator.getResult(ImmutableSet.of("SCHEMA::OWNER",
"CATALOG::USE_CATALOG")));
+ assertTrue(
+ mockEvaluator.getResult(
+ ImmutableSet.of("SCHEMA::OWNER", "SCHEMA::USE_SCHEMA",
"CATALOG::USE_CATALOG")));
+ }
+}
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
index b2a97ffe7e..53b1e91536 100644
---
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
@@ -68,12 +68,12 @@ public class TestModelAuthorizationExpression {
@Test
public void testLoadModel() throws OgnlException, NoSuchFieldException,
IllegalAccessException {
- Field loadTableAuthorizationExpressionField =
+ Field loadModelAuthorizationExpressionField =
ModelOperations.class.getDeclaredField("loadModelAuthorizationExpression");
- loadTableAuthorizationExpressionField.setAccessible(true);
- String loadTableAuthExpression = (String)
loadTableAuthorizationExpressionField.get(null);
+ loadModelAuthorizationExpressionField.setAccessible(true);
+ String loadModelAuthExpression = (String)
loadModelAuthorizationExpressionField.get(null);
MockAuthorizationExpressionEvaluator mockEvaluator =
- new MockAuthorizationExpressionEvaluator(loadTableAuthExpression);
+ new MockAuthorizationExpressionEvaluator(loadModelAuthExpression);
assertFalse(mockEvaluator.getResult(ImmutableSet.of()));
assertTrue(mockEvaluator.getResult(ImmutableSet.of("METALAKE::OWNER")));
assertTrue(mockEvaluator.getResult(ImmutableSet.of("CATALOG::OWNER")));