This is an automated email from the ASF dual-hosted git repository.
liuxun 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 ded16adb05 [#7737] feat(authz):Support
user/role/permission/grant/owner authorization (#7736)
ded16adb05 is described below
commit ded16adb0565386734c23a1b2c20f9a94cf64776
Author: yangyang zhong <[email protected]>
AuthorDate: Fri Jul 25 15:31:32 2025 +0800
[#7737] feat(authz):Support user/role/permission/grant/owner authorization
(#7736)
### What changes were proposed in this pull request?
Support user、role、permission、grant operation authorization
### Why are the changes needed?
close #7737
### Does this PR introduce _any_ user-facing change?
None
### How was this patch tested?
org.apache.gravitino.client.integration.test.authorization.UserAuthorizationIT
org.apache.gravitino.client.integration.test.authorization.PermissionAuthorizationIT
org.apache.gravitino.client.integration.test.authorization.RoleAuthorizationIT
org.apache.gravitino.client.integration.test.authorization.GroupAuthorizationIT
org.apache.gravitino.client.integration.test.authorization.OwnerAuthorizationIT
---
.../test/authorization/GroupAuthorizationIT.java | 73 ++++++
.../test/authorization/OwnerAuthorizationIT.java | 281 +++++++++++++++++++++
.../authorization/PermissionAuthorizationIT.java | 112 ++++++++
.../test/authorization/RoleAuthorizationIT.java | 148 +++++++++++
.../test/authorization/UserAuthorizationIT.java | 134 ++++++++++
.../org/apache/gravitino/auth/AuthConstants.java | 3 +
.../authorization/GravitinoAuthorizer.java | 44 +++-
.../hook/AccessControlHookDispatcher.java | 22 +-
.../server/authorization/MetadataFilterHelper.java | 1 +
.../authorization/PassThroughAuthorizer.java | 20 ++
.../annotations/AuthorizationMetadata.java | 4 +-
.../AuthorizationExpressionConverter.java | 36 ++-
.../AuthorizationExpressionEvaluator.java | 33 ++-
.../authorization/jcasbin/JcasbinAuthorizer.java | 167 +++++++++---
.../src/main/resources/jcasbin_model.conf | 2 +-
.../authorization/MockGravitinoAuthorizer.java | 26 ++
.../server/authorization/TestJcasbinModel.java | 12 +-
.../authorization/annotations/TestAnnotations.java | 13 +-
.../web/filter/GravitinoInterceptionService.java | 68 ++++-
.../server/web/rest/CatalogOperations.java | 20 +-
.../server/web/rest/FilesetOperations.java | 53 ++--
.../gravitino/server/web/rest/GroupOperations.java | 14 +-
.../server/web/rest/MetalakeOperations.java | 9 +-
.../gravitino/server/web/rest/ModelOperations.java | 97 ++++---
.../gravitino/server/web/rest/OwnerOperations.java | 4 +
.../server/web/rest/PermissionOperations.java | 33 ++-
.../gravitino/server/web/rest/RoleOperations.java | 39 ++-
.../server/web/rest/SchemaOperations.java | 27 +-
.../gravitino/server/web/rest/TableOperations.java | 34 ++-
.../gravitino/server/web/rest/TopicOperations.java | 34 ++-
.../gravitino/server/web/rest/UserOperations.java | 49 +++-
.../filter/TestGravitinoInterceptionService.java | 28 +-
.../server/web/rest/TestGroupOperations.java | 3 +-
.../server/web/rest/TestRoleOperations.java | 3 +-
.../server/web/rest/TestUserOperations.java | 3 +-
35 files changed, 1393 insertions(+), 256 deletions(-)
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/GroupAuthorizationIT.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/GroupAuthorizationIT.java
new file mode 100644
index 0000000000..ea995ad86e
--- /dev/null
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/GroupAuthorizationIT.java
@@ -0,0 +1,73 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+import java.util.Collections;
+import java.util.HashMap;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.authorization.Privileges;
+import org.apache.gravitino.client.GravitinoMetalake;
+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 GroupAuthorizationIT extends BaseRestApiAuthorizationIT {
+
+ @Test
+ @Order(1)
+ public void testCreateGroup() {
+ assertThrows(
+ "Current user access metadata {testMetalake}",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).addGroup("group1");
+ });
+ client.loadMetalake(METALAKE).addGroup("group1");
+ client.loadMetalake(METALAKE).addGroup("group2");
+ }
+
+ @Test
+ @Order(2)
+ public void testRemoveGroup() {
+ assertThrows(
+ "Current user access metadata {testMetalake}",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).removeGroup("group1");
+ });
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ // owner can remove user
+ gravitinoMetalake.removeGroup("group1");
+ // user1 can remove group after grant
+ gravitinoMetalake.createRole("role", new HashMap<>(),
Collections.emptyList());
+ gravitinoMetalake.grantPrivilegesToRole(
+ "role",
+ MetadataObjects.of(null, METALAKE, MetadataObject.Type.METALAKE),
+ ImmutableList.of(Privileges.ManageGroups.allow()));
+ gravitinoMetalake.grantRolesToUser(ImmutableList.of("role"), NORMAL_USER);
+ normalUserClient.loadMetalake(METALAKE).removeGroup("group2");
+ }
+}
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerAuthorizationIT.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerAuthorizationIT.java
new file mode 100644
index 0000000000..019c571072
--- /dev/null
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/OwnerAuthorizationIT.java
@@ -0,0 +1,281 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+import com.google.common.collect.Maps;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.authorization.Owner;
+import org.apache.gravitino.authorization.Privileges;
+import org.apache.gravitino.client.GravitinoMetalake;
+import org.apache.gravitino.integration.test.container.ContainerSuite;
+import org.apache.gravitino.integration.test.container.HiveContainer;
+import org.apache.gravitino.rel.Column;
+import org.apache.gravitino.rel.TableCatalog;
+import org.apache.gravitino.rel.types.Types;
+import org.junit.jupiter.api.AfterAll;
+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 OwnerAuthorizationIT extends BaseRestApiAuthorizationIT {
+
+ private static final String CATALOG = "catalog";
+ private static final String SCHEMA = "schema";
+ private static final ContainerSuite containerSuite =
ContainerSuite.getInstance();
+ private static String hmsUri;
+ private static final String role = "role";
+ private static final String TEMP_USER = "user3";
+
+ @BeforeAll
+ public void startIntegrationTest() throws Exception {
+ containerSuite.startHiveContainer();
+ super.startIntegrationTest();
+ hmsUri =
+ String.format(
+ "thrift://%s:%d",
+ containerSuite.getHiveContainer().getContainerIpAddress(),
+ HiveContainer.HIVE_METASTORE_PORT);
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put("metastore.uris", hmsUri);
+ client
+ .loadMetalake(METALAKE)
+ .createCatalog(CATALOG, Catalog.Type.RELATIONAL, "hive", "comment",
properties)
+ .asSchemas()
+ .createSchema(SCHEMA, "test", new HashMap<>());
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ gravitinoMetalake.createRole(role, new HashMap<>(),
Collections.emptyList());
+ gravitinoMetalake.grantRolesToUser(ImmutableList.of(role), NORMAL_USER);
+ client.loadMetalake(METALAKE).addUser(TEMP_USER);
+ TableCatalog tableCatalog =
client.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ tableCatalog.createTable(
+ NameIdentifier.of(SCHEMA, "table1"),
+ new Column[] {Column.of("col1", Types.StringType.get())},
+ "test",
+ new HashMap<>());
+ }
+
+ @AfterAll
+ @Override
+ public void stopIntegrationTest() throws IOException, InterruptedException {
+
client.loadMetalake(METALAKE).loadCatalog(CATALOG).asSchemas().dropSchema(SCHEMA,
true);
+ super.stopIntegrationTest();
+ }
+
+ @Test
+ @Order(1)
+ public void testSetTableOwnerByMetalakeOwner() {
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ USER,
+ Owner.Type.USER);
+ // normal user can not set owner
+ GravitinoMetalake gravitinoMetalakeLoadByNormalUser =
normalUserClient.loadMetalake(METALAKE);
+ assertThrows(
+ "Current user can not set owner",
+ RuntimeException.class,
+ () -> {
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ });
+ }
+
+ @Test
+ @Order(2)
+ public void testSetTableOwnerByCatalogOwner() {
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ USER,
+ Owner.Type.USER);
+ // normal user can not set owner
+ GravitinoMetalake gravitinoMetalakeLoadByNormalUser =
normalUserClient.loadMetalake(METALAKE);
+ assertThrows(
+ "Current user can not set owner",
+ RuntimeException.class,
+ () -> {
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ });
+ // set catalog owner to normal user
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG),
MetadataObject.Type.CATALOG),
+ NORMAL_USER,
+ Owner.Type.USER);
+ // normal user can set owner
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+
+ // reset owner
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG),
MetadataObject.Type.CATALOG),
+ USER,
+ Owner.Type.USER);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ USER,
+ Owner.Type.USER);
+ }
+
+ @Test
+ @Order(3)
+ public void testSetTableOwnerBySchemaOwner() {
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ USER,
+ Owner.Type.USER);
+ // normal user can not set owner
+ GravitinoMetalake gravitinoMetalakeLoadByNormalUser =
normalUserClient.loadMetalake(METALAKE);
+ assertThrows(
+ "Current user can not set owner",
+ RuntimeException.class,
+ () -> {
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ });
+ // set schema owner to normal user
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA),
MetadataObject.Type.SCHEMA),
+ NORMAL_USER,
+ Owner.Type.USER);
+ // normal user can set owner
+ assertThrows(
+ "Current user can not set owner",
+ RuntimeException.class,
+ () -> {
+ // NORMAL_USER has not USE_CATALOG
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ });
+ gravitinoMetalake.grantPrivilegesToRole(
+ role,
+ MetadataObjects.of(ImmutableList.of(CATALOG),
MetadataObject.Type.CATALOG),
+ ImmutableList.of(Privileges.UseCatalog.allow()));
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+
+ // reset owner
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA),
MetadataObject.Type.SCHEMA),
+ USER,
+ Owner.Type.USER);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ USER,
+ Owner.Type.USER);
+ }
+
+ @Test
+ public void testSetTableOwnerByTableOwner() {
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ USER,
+ Owner.Type.USER);
+ // normal user can not set owner
+ GravitinoMetalake gravitinoMetalakeLoadByNormalUser =
normalUserClient.loadMetalake(METALAKE);
+ assertThrows(
+ "Current user can not set owner",
+ RuntimeException.class,
+ () -> {
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ });
+ // set schema owner to normal user
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ assertThrows(
+ "Current user can not set owner",
+ RuntimeException.class,
+ () -> {
+ // NORMAL_USER has not USE_SCHEMA
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(
+ ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+ });
+ gravitinoMetalake.grantPrivilegesToRole(
+ role,
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA),
MetadataObject.Type.SCHEMA),
+ ImmutableList.of(Privileges.UseSchema.allow()));
+ gravitinoMetalakeLoadByNormalUser.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ NORMAL_USER,
+ Owner.Type.USER);
+
+ // reset owner
+ gravitinoMetalake.setOwner(
+ MetadataObjects.of(ImmutableList.of(CATALOG, SCHEMA, "table1"),
MetadataObject.Type.TABLE),
+ USER,
+ Owner.Type.USER);
+ }
+}
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/PermissionAuthorizationIT.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/PermissionAuthorizationIT.java
new file mode 100644
index 0000000000..d353415be1
--- /dev/null
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/PermissionAuthorizationIT.java
@@ -0,0 +1,112 @@
+/*
+ * 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 com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import java.util.HashMap;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.authorization.Privileges;
+import org.apache.gravitino.client.GravitinoMetalake;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
+
+@Tag("gravitino-docker-test")
+public class PermissionAuthorizationIT extends BaseRestApiAuthorizationIT {
+
+ @Test
+ public void testGrant() {
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ gravitinoMetalake.createRole("role1", new HashMap<>(),
Collections.emptyList());
+ gravitinoMetalake.createRole("role2", new HashMap<>(),
Collections.emptyList());
+ gravitinoMetalake.createRole("role3", new HashMap<>(),
Collections.emptyList());
+ gravitinoMetalake.addGroup("group1");
+ gravitinoMetalake.grantRolesToUser(ImmutableList.of("role1"), NORMAL_USER);
+ gravitinoMetalake.grantRolesToGroup(ImmutableList.of("role2"), "group1");
+ gravitinoMetalake.grantPrivilegesToRole(
+ "role1",
+ MetadataObjects.of(ImmutableList.of(METALAKE),
MetadataObject.Type.METALAKE),
+ ImmutableList.of(Privileges.ManageGrants.allow()));
+ gravitinoMetalake.revokeRolesFromUser(ImmutableList.of("role1"),
NORMAL_USER);
+ gravitinoMetalake.revokeRolesFromGroup(ImmutableList.of("role2"),
"group1");
+ gravitinoMetalake.revokeRolesFromUser(ImmutableList.of("role1"),
NORMAL_USER);
+ GravitinoMetalake gravitinoMetalakeLoadByNormalUser =
normalUserClient.loadMetalake(METALAKE);
+ assertThrows(
+ "Current user can not grant.",
+ RuntimeException.class,
+ () -> {
+ gravitinoMetalakeLoadByNormalUser.grantRolesToUser(
+ ImmutableList.of("role1"), NORMAL_USER);
+ });
+ assertThrows(
+ "Current user can not grant.",
+ RuntimeException.class,
+ () -> {
+
gravitinoMetalakeLoadByNormalUser.grantRolesToGroup(ImmutableList.of("role2"),
"group1");
+ });
+ assertThrows(
+ "Current user can not grant.",
+ RuntimeException.class,
+ () -> {
+
gravitinoMetalakeLoadByNormalUser.grantRolesToGroup(ImmutableList.of("role2"),
"group1");
+ });
+ assertThrows(
+ "Current user can not grant.",
+ RuntimeException.class,
+ () -> {
+ gravitinoMetalakeLoadByNormalUser.revokePrivilegesFromRole(
+ "role1",
+ MetadataObjects.of(ImmutableList.of(METALAKE),
MetadataObject.Type.METALAKE),
+ ImmutableSet.of(Privileges.ManageUsers.allow()));
+ });
+ assertThrows(
+ "Current user can not grant.",
+ RuntimeException.class,
+ () -> {
+ gravitinoMetalakeLoadByNormalUser.revokeRolesFromGroup(
+ ImmutableList.of("role2"), "group1");
+ });
+ assertThrows(
+ "Current user can not grant.",
+ RuntimeException.class,
+ () -> {
+ gravitinoMetalakeLoadByNormalUser.revokeRolesFromUser(
+ ImmutableList.of("role1"), NORMAL_USER);
+ });
+
+ gravitinoMetalake.grantRolesToUser(ImmutableList.of("role3"), NORMAL_USER);
+ gravitinoMetalake.grantPrivilegesToRole(
+ "role3",
+ MetadataObjects.of(ImmutableList.of(METALAKE),
MetadataObject.Type.METALAKE),
+ ImmutableList.of(Privileges.ManageGrants.allow()));
+ // normal user can grant after grant
+
gravitinoMetalakeLoadByNormalUser.grantRolesToUser(ImmutableList.of("role1"),
NORMAL_USER);
+
gravitinoMetalakeLoadByNormalUser.grantRolesToGroup(ImmutableList.of("role2"),
"group1");
+
gravitinoMetalakeLoadByNormalUser.grantRolesToGroup(ImmutableList.of("role2"),
"group1");
+ gravitinoMetalakeLoadByNormalUser.revokePrivilegesFromRole(
+ "role1",
+ MetadataObjects.of(ImmutableList.of(METALAKE),
MetadataObject.Type.METALAKE),
+ ImmutableSet.of(Privileges.ManageUsers.allow()));
+
gravitinoMetalakeLoadByNormalUser.revokeRolesFromGroup(ImmutableList.of("role2"),
"group1");
+
gravitinoMetalakeLoadByNormalUser.revokeRolesFromUser(ImmutableList.of("role1"),
NORMAL_USER);
+ }
+}
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/RoleAuthorizationIT.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/RoleAuthorizationIT.java
new file mode 100644
index 0000000000..746545b852
--- /dev/null
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/RoleAuthorizationIT.java
@@ -0,0 +1,148 @@
+/*
+ * 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.assertArrayEquals;
+import static org.junit.Assert.assertThrows;
+
+import java.util.Collections;
+import java.util.HashMap;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.authorization.Privileges;
+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;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
+
+@Tag("gravitino-docker-test")
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class RoleAuthorizationIT extends BaseRestApiAuthorizationIT {
+
+ @Test
+ @Order(1)
+ public void testCreateRole() {
+ client.loadMetalake(METALAKE).createRole("role1", new HashMap<>(),
Collections.emptyList());
+ client.loadMetalake(METALAKE).createRole("role2", new HashMap<>(),
Collections.emptyList());
+ client.loadMetalake(METALAKE).createRole("role3", new HashMap<>(),
Collections.emptyList());
+ // normal user can not create role
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient
+ .loadMetalake(METALAKE)
+ .createRole("role2", new HashMap<>(), Collections.emptyList());
+ });
+ client.loadMetalake(METALAKE).grantRolesToUser(ImmutableList.of("role1"),
NORMAL_USER);
+ client
+ .loadMetalake(METALAKE)
+ .grantPrivilegesToRole(
+ "role1",
+ MetadataObjects.of(ImmutableList.of(METALAKE),
MetadataObject.Type.METALAKE),
+ ImmutableList.of(Privileges.CreateRole.allow()));
+ // normal user can create role after grant
+ normalUserClient
+ .loadMetalake(METALAKE)
+ .createRole("role4", new HashMap<>(), Collections.emptyList());
+ }
+
+ @Test
+ @Order(2)
+ public void testListRole() {
+ String[] roleNames = client.loadMetalake(METALAKE).listRoleNames();
+ assertArrayEquals(new String[] {"role1", "role2", "role3", "role4"},
roleNames);
+ roleNames = normalUserClient.loadMetalake(METALAKE).listRoleNames();
+ assertArrayEquals(new String[] {"role1"}, roleNames);
+ }
+
+ @Test
+ @Order(3)
+ public void testGetRole() {
+ client.loadMetalake(METALAKE).getRole("role1");
+ client.loadMetalake(METALAKE).getRole("role2");
+ client.loadMetalake(METALAKE).getRole("role3");
+ client.loadMetalake(METALAKE).getRole("role4");
+ normalUserClient.loadMetalake(METALAKE).getRole("role1");
+ // normal user can not get role
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).getRole("role2");
+ });
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).getRole("role3");
+ });
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).getRole("role4");
+ });
+ }
+
+ @Test
+ @Order(4)
+ public void testDeleteRole() {
+ // normal user can not delete role
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).deleteRole("role1");
+ });
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).deleteRole("role2");
+ });
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).deleteRole("role3");
+ });
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).deleteRole("role4");
+ });
+ // owner can delete role
+ client.loadMetalake(METALAKE).deleteRole("role1");
+ client.loadMetalake(METALAKE).deleteRole("role2");
+ client.loadMetalake(METALAKE).deleteRole("role3");
+ client.loadMetalake(METALAKE).deleteRole("role4");
+ // normal user can not create role after delete role
+ assertThrows(
+ "Current user can not create role.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient
+ .loadMetalake(METALAKE)
+ .createRole("role2", new HashMap<>(), Collections.emptyList());
+ });
+ }
+}
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/UserAuthorizationIT.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/UserAuthorizationIT.java
new file mode 100644
index 0000000000..225599fd3b
--- /dev/null
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/UserAuthorizationIT.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.gravitino.client.integration.test.authorization;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.authorization.Privileges;
+import org.apache.gravitino.authorization.User;
+import org.apache.gravitino.client.GravitinoAdminClient;
+import org.apache.gravitino.client.GravitinoMetalake;
+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 UserAuthorizationIT extends BaseRestApiAuthorizationIT {
+
+ @Test
+ @Order(1)
+ public void testCreateUser() {
+ assertThrows(
+ "Current user can not access metadata {testMetalake}",
+ RuntimeException.class,
+ () -> {
+ normalUserClient.loadMetalake(METALAKE).addUser("user1");
+ });
+ client.loadMetalake(METALAKE).addUser("user1");
+ client.loadMetalake(METALAKE).addUser("user2");
+ }
+
+ @Test
+ @Order(2)
+ public void testListUser() {
+ User[] users = client.loadMetalake(METALAKE).listUsers();
+ assertUserEquals(new String[] {USER, NORMAL_USER, "user1", "user2"},
users);
+ User[] usersLoadByUser1 =
getClientByUser("user1").loadMetalake(METALAKE).listUsers();
+ assertUserEquals(
+ new String[] {
+ "user1",
+ },
+ usersLoadByUser1);
+ }
+
+ @Test
+ @Order(2)
+ public void testLoadUser() {
+ GravitinoAdminClient user1Client = getClientByUser("user1");
+ user1Client.loadMetalake(METALAKE).getUser("user1");
+ assertThrows(
+ "Current user can not get user.",
+ RuntimeException.class,
+ () -> {
+ user1Client.loadMetalake(METALAKE).getUser("user2");
+ });
+ client.loadMetalake(METALAKE).getUser("user1");
+ client.loadMetalake(METALAKE).getUser("user2");
+ }
+
+ @Test
+ @Order(3)
+ public void testRemoveUser() {
+ GravitinoAdminClient user1Client = getClientByUser("user1");
+ assertThrows(
+ "Current user can not get user",
+ RuntimeException.class,
+ () -> {
+ user1Client.loadMetalake(METALAKE).removeUser("user2");
+ });
+ assertThrows(
+ "Current user can not get user.",
+ RuntimeException.class,
+ () -> {
+ user1Client.loadMetalake(METALAKE).removeUser("user1");
+ });
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ // owner can remove user
+ gravitinoMetalake.removeUser("user2");
+ // user1 can remove user after grant
+ gravitinoMetalake.createRole("role", new HashMap<>(),
Collections.emptyList());
+ gravitinoMetalake.grantPrivilegesToRole(
+ "role",
+ MetadataObjects.of(null, METALAKE, MetadataObject.Type.METALAKE),
+ ImmutableList.of(Privileges.ManageUsers.allow()));
+ gravitinoMetalake.grantRolesToUser(ImmutableList.of("role"), "user1");
+ gravitinoMetalake.addUser("user3");
+ User[] users = gravitinoMetalake.listUsers();
+ assertUserEquals(new String[] {USER, NORMAL_USER, "user1", "user3"},
users);
+ user1Client.loadMetalake(METALAKE).removeUser("user3");
+ users = gravitinoMetalake.listUsers();
+ assertUserEquals(new String[] {USER, NORMAL_USER, "user1"}, users);
+ }
+
+ private void assertUserEquals(String[] exceptUsers, User[] actualUsers) {
+ Arrays.sort(exceptUsers);
+ Arrays.sort(actualUsers, Comparator.comparing(User::name));
+ assertEquals(exceptUsers.length, actualUsers.length);
+ for (int i = 0; i < exceptUsers.length; i++) {
+ assertEquals(exceptUsers[i], actualUsers[i].name());
+ }
+ }
+
+ private GravitinoAdminClient getClientByUser(String username) {
+ GravitinoAdminClient client =
+
GravitinoAdminClient.builder(serverUri).withSimpleAuth(username).build();
+ closer.register(client);
+ return client;
+ }
+}
diff --git a/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
b/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
index 38ce4ac2b0..46b4e5964f 100644
--- a/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
+++ b/common/src/main/java/org/apache/gravitino/auth/AuthConstants.java
@@ -47,6 +47,9 @@ public final class AuthConstants {
/** OWNER. */
public static final String OWNER = "OWNER";
+ /** SELF authorization expression. */
+ public static final String SELF = "SELF";
+
/**
* The default name of the attribute that stores the authenticated principal
in the request.
*
diff --git
a/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
b/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
index 7a8a11f339..1887ffea08 100644
---
a/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
+++
b/core/src/main/java/org/apache/gravitino/authorization/GravitinoAuthorizer.java
@@ -65,9 +65,17 @@ public interface GravitinoAuthorizer extends Closeable {
*
* @return authorization result
*/
- default boolean isServiceAdmin() {
- return true;
- }
+ boolean isServiceAdmin();
+
+ /**
+ * Determine whether the user accessing is oneself, or whether the group
being accessed contains
+ * oneself.
+ *
+ * @param type user or group
+ * @param nameIdentifier name of user or group
+ * @return authorization result
+ */
+ boolean isSelf(Entity.EntityType type, NameIdentifier nameIdentifier);
/**
* Determine whether the user is the metalake user
@@ -75,9 +83,27 @@ public interface GravitinoAuthorizer extends Closeable {
* @param metalake metalake
* @return authorization result
*/
- default boolean isMetalakeUser(String metalake) {
- return true;
- };
+ boolean isMetalakeUser(String metalake);
+
+ /**
+ * Determine whether the user can set owner
+ *
+ * @param metalake metalake
+ * @param type metadata type
+ * @param fullName metadata full name
+ * @return authorization result
+ */
+ boolean hasSetOwnerPermission(String metalake, String type, String fullName);
+
+ /**
+ * Determine whether the user can grant or revoke privilege for metadata
+ *
+ * @param metalake metalake
+ * @param type metadata type
+ * @param fullName metadata full name
+ * @return authorization result
+ */
+ boolean hasMetadataPrivilegePermission(String metalake, String type, String
fullName);
/**
* When the permissions of a role change, it is necessary to notify the
GravitinoAuthorizer in
@@ -85,7 +111,7 @@ public interface GravitinoAuthorizer extends Closeable {
*
* @param roleId The role id;
*/
- default void handleRolePrivilegeChange(Long roleId) {};
+ void handleRolePrivilegeChange(Long roleId);
/**
* When the permissions of a role change, it is necessary to notify the
GravitinoAuthorizer in
@@ -118,6 +144,6 @@ public interface GravitinoAuthorizer extends Closeable {
* @param nameIdentifier The metadata name identifier;
* @param type entity type
*/
- default void handleMetadataOwnerChange(
- String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type) {};
+ void handleMetadataOwnerChange(
+ String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type);
}
diff --git
a/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
b/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
index 40753b9b91..83413d73da 100644
---
a/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
+++
b/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.hook;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import lombok.extern.slf4j.Slf4j;
import org.apache.gravitino.Entity;
import org.apache.gravitino.GravitinoEnv;
import org.apache.gravitino.MetadataObject;
@@ -43,6 +44,7 @@ import org.apache.gravitino.exceptions.NoSuchRoleException;
import org.apache.gravitino.exceptions.NoSuchUserException;
import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
import org.apache.gravitino.exceptions.UserAlreadyExistsException;
+import org.apache.gravitino.meta.RoleEntity;
import org.apache.gravitino.utils.NameIdentifierUtil;
import org.apache.gravitino.utils.PrincipalUtils;
@@ -51,6 +53,7 @@ import org.apache.gravitino.utils.PrincipalUtils;
* only delegates access control operations to the underlying access control
dispatcher but also
* executes some hook operations before or after the underlying operations.
*/
+@Slf4j
public class AccessControlHookDispatcher implements AccessControlDispatcher {
private final AccessControlDispatcher dispatcher;
@@ -178,7 +181,17 @@ public class AccessControlHookDispatcher implements
AccessControlDispatcher {
@Override
public boolean deleteRole(String metalake, String role) throws
NoSuchMetalakeException {
- return dispatcher.deleteRole(metalake, role);
+ Role oldRole = null;
+ try {
+ oldRole = getRole(metalake, role);
+ } catch (NoSuchRoleException e) {
+ log.debug(e.getMessage());
+ }
+ boolean resultOfDeleteRole = dispatcher.deleteRole(metalake, role);
+ if (oldRole != null) {
+ notifyRoleUserRelChange(((RoleEntity) oldRole).id());
+ }
+ return resultOfDeleteRole;
}
@Override
@@ -225,4 +238,11 @@ public class AccessControlHookDispatcher implements
AccessControlDispatcher {
gravitinoAuthorizer.handleRolePrivilegeChange(metalake, role);
}
}
+
+ private static void notifyRoleUserRelChange(Long role) {
+ GravitinoAuthorizer gravitinoAuthorizer =
GravitinoEnv.getInstance().gravitinoAuthorizer();
+ if (gravitinoAuthorizer != null) {
+ gravitinoAuthorizer.handleRolePrivilegeChange(role);
+ }
+ }
}
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 066c550c40..5ff3237b52 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
@@ -152,6 +152,7 @@ public class MetadataFilterHelper {
Entity.EntityType.CATALOG,
NameIdentifierUtil.getCatalogIdentifier(nameIdentifier));
break;
default:
+ nameIdentifierMap.put(entityType, nameIdentifier);
break;
}
return nameIdentifierMap;
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
b/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
index d98b04825d..f28e402128 100644
---
a/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
+++
b/server-common/src/main/java/org/apache/gravitino/server/authorization/PassThroughAuthorizer.java
@@ -53,6 +53,26 @@ public class PassThroughAuthorizer implements
GravitinoAuthorizer {
return true;
}
+ @Override
+ public boolean isSelf(Entity.EntityType type, NameIdentifier nameIdentifier)
{
+ return true;
+ }
+
+ @Override
+ public boolean isMetalakeUser(String metalake) {
+ return true;
+ }
+
+ @Override
+ public boolean hasSetOwnerPermission(String metalake, String type, String
fullName) {
+ return true;
+ }
+
+ @Override
+ public boolean hasMetadataPrivilegePermission(String metalake, String type,
String fullName) {
+ return true;
+ }
+
@Override
public void handleRolePrivilegeChange(Long roleId) {}
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadata.java
b/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadata.java
index 41d7a3f52b..5d4978cf0c 100644
---
a/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadata.java
+++
b/server-common/src/main/java/org/apache/gravitino/server/authorization/annotations/AuthorizationMetadata.java
@@ -22,7 +22,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.Entity;
/** This annotation identify which parameters in the request are to be used
for authorization. */
@Target({ElementType.PARAMETER, ElementType.FIELD})
@@ -33,5 +33,5 @@ public @interface AuthorizationMetadata {
*
* @return the type of the parameter to be used for authorization.
*/
- MetadataObject.Type type();
+ Entity.EntityType type();
}
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 71054c6a34..b72d904761 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
@@ -35,6 +35,20 @@ public class AuthorizationExpressionConverter {
/** Match ANY expressions */
public static final Pattern ANY_PATTERN =
Pattern.compile("ANY\\(([^)]+)\\)");
+ /**
+ * This authorization expression will invoke the
`hasMetadataPrivilegePermission` method of
+ * `GravitinoAuthorizer` to perform the access control check, and return the
result of the
+ * authorization.
+ */
+ public static final String CAN_OPERATE_METADATA_PRIVILEGE =
"CAN_OPERATE_METADATA_PRIVILEGE";
+
+ /**
+ * This authorization expression will invoke the `hasSetOwnerPermission`
method of
+ * `GravitinoAuthorizer` to perform the access control check, and return the
result of the
+ * authorization.
+ */
+ public static final String CAN_SET_OWNER = "CAN_SET_OWNER";
+
/**
* The EXPRESSION_CACHE caches the result of converting authorization
expressions into an OGNL
* expression.
@@ -63,18 +77,22 @@ public class AuthorizationExpressionConverter {
StringBuffer result = new StringBuffer();
while (matcher.find()) {
- String metadataType = matcher.group(1);
- String privilegeOrOwner = matcher.group(2);
+ String type = matcher.group(1);
+ String privilegeOrExpression = matcher.group(2);
String replacement;
- if (AuthConstants.OWNER.equals(privilegeOrOwner)) {
+ if (AuthConstants.OWNER.equals(privilegeOrExpression)) {
+ replacement =
String.format("authorizer.isOwner(principal,METALAKE_NAME,%s)", type);
+ } else if (AuthConstants.SELF.equals(privilegeOrExpression)) {
replacement =
-
String.format("authorizer.isOwner(principal,METALAKE_NAME,%s)", metadataType);
+ String.format(
+
"authorizer.isSelf(@org.apache.gravitino.Entity\\$EntityType@%s,%s_NAME_IDENT)",
+ type, type);
} else {
replacement =
String.format(
"authorizer.authorize(principal,METALAKE_NAME,%s,"
+
"@org.apache.gravitino.authorization.Privilege\\$Name@%s)",
- metadataType, privilegeOrOwner);
+ type, privilegeOrExpression);
}
matcher.appendReplacement(result, replacement);
}
@@ -171,6 +189,14 @@ public class AuthorizationExpressionConverter {
expression =
expression.replaceAll(
"ANY_WRITE_FILESET", "(ANY(WRITE_FILESET, METALAKE, CATALOG,
SCHEMA, FILESET))");
+ expression =
+ expression.replaceAll(
+ CAN_SET_OWNER,
+
"authorizer.hasSetOwnerPermission(p_metalake,p_metadataObjectType,p_fullName)");
+ expression =
+ expression.replaceAll(
+ CAN_OPERATE_METADATA_PRIVILEGE,
+
"authorizer.hasMetadataPrivilegePermission(p_metalake,p_metadataObjectType,p_fullName)");
return expression;
}
}
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionEvaluator.java
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionEvaluator.java
index cca38ccd0e..c83b1a0590 100644
---
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionEvaluator.java
+++
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionEvaluator.java
@@ -18,7 +18,10 @@
package org.apache.gravitino.server.authorization.expression;
import java.security.Principal;
+import java.util.Arrays;
+import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import ognl.Ognl;
import ognl.OgnlContext;
@@ -55,17 +58,34 @@ public class AuthorizationExpressionEvaluator {
* @return authorization result
*/
public boolean evaluate(Map<Entity.EntityType, NameIdentifier>
metadataNames) {
+ return evaluate(metadataNames, new HashMap<>());
+ }
+
+ /**
+ * Use OGNL expressions to invoke GravitinoAuthorizer for authorizing
multiple types of metadata
+ * IDs.
+ *
+ * @param metadataNames key-metadata type, value-metadata NameIdentifier
+ * @param pathParams params from request path
+ * @return authorization result
+ */
+ public boolean evaluate(
+ Map<Entity.EntityType, NameIdentifier> metadataNames, Map<String,
Object> pathParams) {
Principal currentPrincipal = PrincipalUtils.getCurrentPrincipal();
GravitinoAuthorizer gravitinoAuthorizer =
GravitinoAuthorizerProvider.getInstance().getGravitinoAuthorizer();
OgnlContext ognlContext = Ognl.createDefaultContext(null);
ognlContext.put("principal", currentPrincipal);
ognlContext.put("authorizer", gravitinoAuthorizer);
+ ognlContext.putAll(pathParams);
metadataNames.forEach(
- (metadataType, metadataName) -> {
- MetadataObject metadataObject =
- NameIdentifierUtil.toMetadataObject(metadataName, metadataType);
- ognlContext.put(metadataType.name(), metadataObject);
+ (type, entityNameIdent) -> {
+ if (isMetadataType(type)) {
+ MetadataObject metadataObject =
+ NameIdentifierUtil.toMetadataObject(entityNameIdent, type);
+ ognlContext.put(type.name(), metadataObject);
+ }
+ ognlContext.put(type.name() + "_NAME_IDENT", entityNameIdent);
});
NameIdentifier nameIdentifier =
metadataNames.get(Entity.EntityType.METALAKE);
ognlContext.put(
@@ -77,4 +97,9 @@ public class AuthorizationExpressionEvaluator {
throw new RuntimeException("ognl evaluate error", e);
}
}
+
+ private static boolean isMetadataType(Entity.EntityType type) {
+ return Arrays.stream(MetadataObject.Type.values())
+ .anyMatch(e -> Objects.equals(e.name(), type.name()));
+ }
}
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
b/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
index aea414ebaf..d8f260a638 100644
---
a/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
+++
b/server-common/src/main/java/org/apache/gravitino/server/authorization/jcasbin/JcasbinAuthorizer.java
@@ -23,7 +23,9 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
+import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.io.IOUtils;
@@ -34,6 +36,7 @@ import org.apache.gravitino.Entity;
import org.apache.gravitino.EntityStore;
import org.apache.gravitino.GravitinoEnv;
import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.SupportsRelationOperations;
import org.apache.gravitino.auth.AuthConstants;
@@ -47,6 +50,7 @@ import org.apache.gravitino.utils.MetadataObjectUtil;
import org.apache.gravitino.utils.NameIdentifierUtil;
import org.apache.gravitino.utils.PrincipalUtils;
import org.casbin.jcasbin.main.Enforcer;
+import org.casbin.jcasbin.main.SyncedEnforcer;
import org.casbin.jcasbin.model.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -75,7 +79,7 @@ public class JcasbinAuthorizer implements GravitinoAuthorizer
{
String modelData = IOUtils.toString(modelStream, StandardCharsets.UTF_8);
Model model = new Model();
model.loadModelFromText(modelData);
- enforcer = new Enforcer(model, new GravitinoAdapter());
+ enforcer = new SyncedEnforcer(model, new GravitinoAdapter());
Config config = GravitinoEnv.getInstance().config();
if (config != null) {
serviceAdmins.addAll(config.get(Configs.SERVICE_ADMINS));
@@ -121,6 +125,98 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
}
}
+ @Override
+ public boolean isSelf(Entity.EntityType type, NameIdentifier nameIdentifier)
{
+ String metalake = nameIdentifier.namespace().level(0);
+ String currentUserName = PrincipalUtils.getCurrentUserName();
+ if (Entity.EntityType.USER == type) {
+ return Objects.equals(nameIdentifier.name(), currentUserName);
+ } else if (Entity.EntityType.ROLE == type) {
+ try {
+ Long roleId =
+ MetadataIdConverter.getID(
+ MetadataObjects.of(
+ String.join(
+ ".",
+ nameIdentifier.namespace().level(1),
+ nameIdentifier.namespace().level(2)),
+ nameIdentifier.name(),
+ MetadataObject.Type.ROLE),
+ metalake);
+ UserEntity userEntity = getUserEntity(currentUserName, metalake);
+ Long userId = userEntity.id();
+ loadRolePrivilege(metalake, currentUserName, userId);
+ return enforcer.hasRoleForUser(String.valueOf(userId),
String.valueOf(roleId));
+ } catch (Exception e) {
+ LOG.warn("can not get user id or role id.", e);
+ return false;
+ }
+ }
+ throw new UnsupportedOperationException("Unsupported Entity Type.");
+ }
+
+ @Override
+ public boolean hasSetOwnerPermission(String metalake, String type, String
fullName) {
+ Principal currentPrincipal = PrincipalUtils.getCurrentPrincipal();
+ MetadataObject metalakeObject =
+ MetadataObjects.of(ImmutableList.of(metalake),
MetadataObject.Type.METALAKE);
+ // metalake owner can set owner in metalake.
+ if (isOwner(currentPrincipal, metalake, metalakeObject)) {
+ return true;
+ }
+ MetadataObject.Type metadataType =
MetadataObject.Type.valueOf(type.toUpperCase());
+ MetadataObject metadataObject =
+ MetadataObjects.of(Arrays.asList(fullName.split("\\.")), metadataType);
+ do {
+ if (isOwner(currentPrincipal, metalake, metadataObject)) {
+ MetadataObject.Type tempType = metadataObject.type();
+ if (tempType == MetadataObject.Type.SCHEMA) {
+ // schema owner need use catalog privilege
+ boolean hasCatalogUseCatalog =
+ authorize(
+ currentPrincipal,
+ metalake,
+ MetadataObjects.parent(metadataObject),
+ Privilege.Name.USE_CATALOG);
+ boolean hasMetalakeUseCatalog =
+ authorize(currentPrincipal, metalake, metalakeObject,
Privilege.Name.USE_CATALOG);
+ return hasCatalogUseCatalog || hasMetalakeUseCatalog;
+ }
+ if (tempType == MetadataObject.Type.TABLE
+ || tempType == MetadataObject.Type.TOPIC
+ || tempType == MetadataObject.Type.FILESET
+ || tempType == MetadataObject.Type.MODEL) {
+ // table owner need use_catalog and use_schema privileges
+ boolean hasMetalakeUseSchema =
+ authorize(currentPrincipal, metalake, metalakeObject,
Privilege.Name.USE_SCHEMA);
+ MetadataObject schemaObject = MetadataObjects.parent(metadataObject);
+ boolean hasCatalogUseSchema =
+ authorize(
+ currentPrincipal,
+ metalake,
+ MetadataObjects.parent(schemaObject),
+ Privilege.Name.USE_SCHEMA);
+ boolean hasSchemaUseSchema =
+ authorize(currentPrincipal, metalake, schemaObject,
Privilege.Name.USE_SCHEMA);
+ return hasMetalakeUseSchema || hasCatalogUseSchema ||
hasSchemaUseSchema;
+ }
+ return true;
+ }
+ // metadata parent owner can set owner.
+ } while ((metadataObject = MetadataObjects.parent(metadataObject)) !=
null);
+ return false;
+ }
+
+ @Override
+ public boolean hasMetadataPrivilegePermission(String metalake, String type,
String fullName) {
+ Principal currentPrincipal = PrincipalUtils.getCurrentPrincipal();
+ MetadataObject metalakeMetadataObject =
+ MetadataObjects.of(ImmutableList.of(metalake),
MetadataObject.Type.METALAKE);
+ return authorize(
+ currentPrincipal, metalake, metalakeMetadataObject,
Privilege.Name.MANAGE_GRANTS)
+ || hasSetOwnerPermission(metalake, type, fullName);
+ }
+
@Override
public void handleRolePrivilegeChange(Long roleId) {
loadedRoles.remove(roleId);
@@ -165,12 +261,7 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
Long metadataId;
Long userId;
try {
- EntityStore entityStore = GravitinoEnv.getInstance().entityStore();
- UserEntity userEntity =
- entityStore.get(
- NameIdentifierUtil.ofUser(metalake, username),
- Entity.EntityType.USER,
- UserEntity.class);
+ UserEntity userEntity = getUserEntity(username, metalake);
userId = userEntity.id();
metadataId = MetadataIdConverter.getID(metadataObject, metalake);
} catch (Exception e) {
@@ -181,6 +272,16 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
return authorizeByJcasbin(userId, metadataObject, metadataId, privilege);
}
+ private static UserEntity getUserEntity(String username, String metalake)
throws IOException {
+ EntityStore entityStore = GravitinoEnv.getInstance().entityStore();
+ UserEntity userEntity =
+ entityStore.get(
+ NameIdentifierUtil.ofUser(metalake, username),
+ Entity.EntityType.USER,
+ UserEntity.class);
+ return userEntity;
+ }
+
private void loadPrivilege(
String metalake,
String username,
@@ -188,36 +289,40 @@ public class JcasbinAuthorizer implements
GravitinoAuthorizer {
MetadataObject metadataObject,
Long metadataObjectId) {
try {
- EntityStore entityStore = GravitinoEnv.getInstance().entityStore();
- NameIdentifier userNameIdentifier = NameIdentifierUtil.ofUser(metalake,
username);
- List<RoleEntity> entities =
- entityStore
- .relationOperations()
- .listEntitiesByRelation(
- SupportsRelationOperations.Type.ROLE_USER_REL,
- userNameIdentifier,
- Entity.EntityType.USER);
-
- for (RoleEntity role : entities) {
- Long roleId = role.id();
- role =
- entityStore.get(
- NameIdentifierUtil.ofRole(metalake, role.name()),
- Entity.EntityType.ROLE,
- RoleEntity.class);
- if (loadedRoles.contains(roleId)) {
- continue;
- }
- enforcer.addRoleForUser(String.valueOf(userId),
String.valueOf(roleId));
- loadPolicyByRoleEntity(role);
- loadedRoles.add(roleId);
- }
+ loadRolePrivilege(metalake, username, userId);
loadOwnerPolicy(metalake, metadataObject, metadataObjectId);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
+ private void loadRolePrivilege(String metalake, String username, Long
userId) throws IOException {
+ EntityStore entityStore = GravitinoEnv.getInstance().entityStore();
+ NameIdentifier userNameIdentifier = NameIdentifierUtil.ofUser(metalake,
username);
+ List<RoleEntity> entities =
+ entityStore
+ .relationOperations()
+ .listEntitiesByRelation(
+ SupportsRelationOperations.Type.ROLE_USER_REL,
+ userNameIdentifier,
+ Entity.EntityType.USER);
+
+ for (RoleEntity role : entities) {
+ Long roleId = role.id();
+ role =
+ entityStore.get(
+ NameIdentifierUtil.ofRole(metalake, role.name()),
+ Entity.EntityType.ROLE,
+ RoleEntity.class);
+ if (loadedRoles.contains(roleId)) {
+ continue;
+ }
+ enforcer.addRoleForUser(String.valueOf(userId), String.valueOf(roleId));
+ loadPolicyByRoleEntity(role);
+ loadedRoles.add(roleId);
+ }
+ }
+
private void loadOwnerPolicy(String metalake, MetadataObject metadataObject,
Long metadataId) {
try {
NameIdentifier entityIdent = MetadataObjectUtil.toEntityIdent(metalake,
metadataObject);
diff --git a/server-common/src/main/resources/jcasbin_model.conf
b/server-common/src/main/resources/jcasbin_model.conf
index 3247012b3b..ba17c87b09 100644
--- a/server-common/src/main/resources/jcasbin_model.conf
+++ b/server-common/src/main/resources/jcasbin_model.conf
@@ -51,5 +51,5 @@ e = some(where (p.eft == allow)) && !some(where (p.eft ==
deny))
; "m" represents the matching rules of the model
[matchers]
-m = g(r.sub, p.sub) && r.metadataId == p.metadataId && r.metadataType ==
p.metadataType && ( p.act == "OWNER" || r.act == p.act )
+m = g(r.sub, p.sub) && r.metadataId == p.metadataId && r.metadataType ==
p.metadataType && ( (p.act == "OWNER" && r.act == "OWNER") || r.act == p.act )
diff --git
a/server-common/src/test/java/org/apache/gravitino/server/authorization/MockGravitinoAuthorizer.java
b/server-common/src/test/java/org/apache/gravitino/server/authorization/MockGravitinoAuthorizer.java
index 8bde1bb209..dfe021b6d9 100644
---
a/server-common/src/test/java/org/apache/gravitino/server/authorization/MockGravitinoAuthorizer.java
+++
b/server-common/src/test/java/org/apache/gravitino/server/authorization/MockGravitinoAuthorizer.java
@@ -19,7 +19,9 @@ package org.apache.gravitino.server.authorization;
import java.security.Principal;
import java.util.Objects;
+import org.apache.gravitino.Entity;
import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.authorization.GravitinoAuthorizer;
import org.apache.gravitino.authorization.Privilege;
@@ -69,9 +71,33 @@ public class MockGravitinoAuthorizer implements
GravitinoAuthorizer {
return false;
}
+ @Override
+ public boolean isSelf(Entity.EntityType type, NameIdentifier nameIdentifier)
{
+ return true;
+ }
+
+ @Override
+ public boolean isMetalakeUser(String metalake) {
+ return true;
+ }
+
+ @Override
+ public boolean hasSetOwnerPermission(String metalake, String type, String
fullName) {
+ return true;
+ }
+
+ @Override
+ public boolean hasMetadataPrivilegePermission(String metalake, String type,
String fullName) {
+ return true;
+ }
+
@Override
public void handleRolePrivilegeChange(Long roleId) {}
+ @Override
+ public void handleMetadataOwnerChange(
+ String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type) {}
+
@Override
public void close() {}
}
diff --git
a/server-common/src/test/java/org/apache/gravitino/server/authorization/TestJcasbinModel.java
b/server-common/src/test/java/org/apache/gravitino/server/authorization/TestJcasbinModel.java
index 43410b8f28..5ddfe35ba2 100644
---
a/server-common/src/test/java/org/apache/gravitino/server/authorization/TestJcasbinModel.java
+++
b/server-common/src/test/java/org/apache/gravitino/server/authorization/TestJcasbinModel.java
@@ -75,7 +75,7 @@ public class TestJcasbinModel {
public void testMetalakeOwner() {
Assertions.assertTrue(
enforcer.enforce("role1", MetadataObject.Type.METALAKE.name(),
"metalake1", "OWNER"));
- Assertions.assertTrue(
+ Assertions.assertFalse(
enforcer.enforce(
"role1",
MetadataObject.Type.METALAKE.name(),
@@ -99,13 +99,13 @@ public class TestJcasbinModel {
public void testCatalogOwner() {
Assertions.assertTrue(
enforcer.enforce("role2", MetadataObject.Type.CATALOG.name(),
"catalog1", "OWNER"));
- Assertions.assertTrue(
+ Assertions.assertFalse(
enforcer.enforce(
"role2",
MetadataObject.Type.CATALOG.name(),
"catalog1",
Privilege.Name.USE_SCHEMA.name()));
- Assertions.assertTrue(
+ Assertions.assertFalse(
enforcer.enforce(
"role2",
MetadataObject.Type.CATALOG.name(),
@@ -128,7 +128,7 @@ public class TestJcasbinModel {
public void testSchemaOwner() {
Assertions.assertTrue(
enforcer.enforce("role3", MetadataObject.Type.SCHEMA.name(),
"schema1", "OWNER"));
- Assertions.assertTrue(
+ Assertions.assertFalse(
enforcer.enforce(
"role3",
MetadataObject.Type.SCHEMA.name(),
@@ -149,13 +149,13 @@ public class TestJcasbinModel {
public void testTableOwner() {
Assertions.assertTrue(
enforcer.enforce("role4", MetadataObject.Type.TABLE.name(), "table1",
"OWNER"));
- Assertions.assertTrue(
+ Assertions.assertFalse(
enforcer.enforce(
"role4",
MetadataObject.Type.TABLE.name(),
"table1",
Privilege.Name.MODIFY_TABLE.name()));
- Assertions.assertTrue(
+ Assertions.assertFalse(
enforcer.enforce(
"role4",
MetadataObject.Type.TABLE.name(),
diff --git
a/server-common/src/test/java/org/apache/gravitino/server/authorization/annotations/TestAnnotations.java
b/server-common/src/test/java/org/apache/gravitino/server/authorization/annotations/TestAnnotations.java
index 556a0ce368..c089119e42 100644
---
a/server-common/src/test/java/org/apache/gravitino/server/authorization/annotations/TestAnnotations.java
+++
b/server-common/src/test/java/org/apache/gravitino/server/authorization/annotations/TestAnnotations.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.server.authorization.annotations;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
+import org.apache.gravitino.Entity;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.authorization.Privilege;
import org.junit.jupiter.api.Assertions;
@@ -32,13 +33,13 @@ public class TestAnnotations {
static class TestResourceAnnotationClass {
public void methodWithAnnotatedParam(
- @AuthorizationMetadata(type = MetadataObject.Type.TABLE) String table)
{
+ @AuthorizationMetadata(type = Entity.EntityType.TABLE) String table) {
// dummy method
}
public void listSchemas(
- @AuthorizationMetadata(type = MetadataObject.Type.METALAKE) String
metalake,
- @AuthorizationMetadata(type = MetadataObject.Type.CATALOG) String
catalog) {
+ @AuthorizationMetadata(type = Entity.EntityType.METALAKE) String
metalake,
+ @AuthorizationMetadata(type = Entity.EntityType.CATALOG) String
catalog) {
// dummy method
}
}
@@ -98,7 +99,7 @@ public class TestAnnotations {
.getParameters()[0];
AuthorizationMetadata annotation =
argument.getAnnotation(AuthorizationMetadata.class);
Assertions.assertNotNull(annotation);
- Assertions.assertEquals(MetadataObject.Type.TABLE, annotation.type());
+ Assertions.assertEquals(Entity.EntityType.TABLE, annotation.type());
}
@Test
@@ -112,12 +113,12 @@ public class TestAnnotations {
AuthorizationMetadata metalakeAnnotation =
argumentMetalake.getAnnotation(AuthorizationMetadata.class);
Assertions.assertNotNull(metalakeAnnotation);
- Assertions.assertEquals(MetadataObject.Type.METALAKE,
metalakeAnnotation.type());
+ Assertions.assertEquals(Entity.EntityType.METALAKE,
metalakeAnnotation.type());
Parameter argumentCatalog = arguments[1];
AuthorizationMetadata catalogAnnotation =
argumentCatalog.getAnnotation(AuthorizationMetadata.class);
Assertions.assertNotNull(catalogAnnotation);
- Assertions.assertEquals(MetadataObject.Type.CATALOG,
catalogAnnotation.type());
+ Assertions.assertEquals(Entity.EntityType.CATALOG,
catalogAnnotation.type());
}
}
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 3c4c557224..811fcd8271 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
@@ -28,6 +28,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import org.aopalliance.intercept.ConstructorInterceptor;
import org.aopalliance.intercept.MethodInterceptor;
@@ -42,11 +43,16 @@ import
org.apache.gravitino.server.authorization.expression.AuthorizationExpress
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.GroupOperations;
import org.apache.gravitino.server.web.rest.MetalakeOperations;
import org.apache.gravitino.server.web.rest.ModelOperations;
+import org.apache.gravitino.server.web.rest.OwnerOperations;
+import org.apache.gravitino.server.web.rest.PermissionOperations;
+import org.apache.gravitino.server.web.rest.RoleOperations;
import org.apache.gravitino.server.web.rest.SchemaOperations;
import org.apache.gravitino.server.web.rest.TableOperations;
import org.apache.gravitino.server.web.rest.TopicOperations;
+import org.apache.gravitino.server.web.rest.UserOperations;
import org.apache.gravitino.utils.NameIdentifierUtil;
import org.apache.gravitino.utils.PrincipalUtils;
import org.glassfish.hk2.api.Descriptor;
@@ -72,7 +78,12 @@ public class GravitinoInterceptionService implements
InterceptionService {
TableOperations.class.getName(),
ModelOperations.class.getName(),
TopicOperations.class.getName(),
- FilesetOperations.class.getName()));
+ FilesetOperations.class.getName(),
+ UserOperations.class.getName(),
+ GroupOperations.class.getName(),
+ PermissionOperations.class.getName(),
+ RoleOperations.class.getName(),
+ OwnerOperations.class.getName()));
}
@Override
@@ -113,9 +124,11 @@ public class GravitinoInterceptionService implements
InterceptionService {
Object[] args = methodInvocation.getArguments();
Map<Entity.EntityType, NameIdentifier> metadataContext =
extractNameIdentifierFromParameters(parameters, args);
+ Map<String, Object> pathParams =
extractPathParamsFromParameters(parameters, args);
AuthorizationExpressionEvaluator authorizationExpressionEvaluator =
new AuthorizationExpressionEvaluator(expression);
- boolean authorizeResult =
authorizationExpressionEvaluator.evaluate(metadataContext);
+ boolean authorizeResult =
+ authorizationExpressionEvaluator.evaluate(metadataContext,
pathParams);
if (!authorizeResult) {
MetadataObject.Type type =
expressionAnnotation.accessMetadataType();
NameIdentifier accessMetadataName =
@@ -172,7 +185,7 @@ public class GravitinoInterceptionService implements
InterceptionService {
private Map<Entity.EntityType, NameIdentifier>
extractNameIdentifierFromParameters(
Parameter[] parameters, Object[] args) {
- Map<Entity.EntityType, String> metadatas = new HashMap<>();
+ Map<Entity.EntityType, String> entities = new HashMap<>();
Map<Entity.EntityType, NameIdentifier> nameIdentifierMap = new
HashMap<>();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
@@ -181,16 +194,16 @@ public class GravitinoInterceptionService implements
InterceptionService {
if (authorizeResource == null) {
continue;
}
- MetadataObject.Type type = authorizeResource.type();
- metadatas.put(Entity.EntityType.valueOf(type.name()),
String.valueOf(args[i]));
+ Entity.EntityType type = authorizeResource.type();
+ entities.put(type, String.valueOf(args[i]));
}
- String metalake = metadatas.get(Entity.EntityType.METALAKE);
- String catalog = metadatas.get(Entity.EntityType.CATALOG);
- 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(
+ String metalake = entities.get(Entity.EntityType.METALAKE);
+ String catalog = entities.get(Entity.EntityType.CATALOG);
+ String schema = entities.get(Entity.EntityType.SCHEMA);
+ String table = entities.get(Entity.EntityType.TABLE);
+ String topic = entities.get(Entity.EntityType.TOPIC);
+ String fileset = entities.get(Entity.EntityType.FILESET);
+ entities.forEach(
(type, metadata) -> {
switch (type) {
case CATALOG:
@@ -218,7 +231,7 @@ public class GravitinoInterceptionService implements
InterceptionService {
NameIdentifierUtil.ofFileset(metalake, catalog, schema,
fileset));
break;
case MODEL:
- String model = metadatas.get(Entity.EntityType.MODEL);
+ String model = entities.get(Entity.EntityType.MODEL);
nameIdentifierMap.put(
Entity.EntityType.MODEL,
NameIdentifierUtil.ofModel(metadata, catalog, schema,
model));
@@ -227,12 +240,41 @@ public class GravitinoInterceptionService implements
InterceptionService {
nameIdentifierMap.put(
Entity.EntityType.METALAKE,
NameIdentifierUtil.ofMetalake(metalake));
break;
+ case USER:
+ nameIdentifierMap.put(
+ Entity.EntityType.USER,
+ NameIdentifierUtil.ofUser(metadata,
entities.get(Entity.EntityType.USER)));
+ break;
+ case GROUP:
+ nameIdentifierMap.put(
+ Entity.EntityType.GROUP,
+ NameIdentifierUtil.ofGroup(metalake,
entities.get(Entity.EntityType.GROUP)));
+ break;
+ case ROLE:
+ nameIdentifierMap.put(
+ Entity.EntityType.ROLE,
+ NameIdentifierUtil.ofRole(metalake,
entities.get(Entity.EntityType.ROLE)));
+ break;
default:
break;
}
});
return nameIdentifierMap;
}
+
+ private Map<String, Object> extractPathParamsFromParameters(
+ Parameter[] parameters, Object[] args) {
+ Map<String, Object> pathParams = new HashMap<>();
+ for (int i = 0; i < parameters.length; i++) {
+ Parameter parameter = parameters[i];
+ PathParam pathParam = parameter.getAnnotation(PathParam.class);
+ if (pathParam == null) {
+ continue;
+ }
+ pathParams.put("p_" + pathParam.value(), args[i]);
+ }
+ return pathParams;
+ }
}
private static class ClassListFilter implements Filter {
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
index c2a36cf0ca..bebec63a60 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/CatalogOperations.java
@@ -150,7 +150,7 @@ public class CatalogOperations {
expression = "METALAKE::CREATE_CATALOG || METALAKE::OWNER",
accessMetadataType = MetadataObject.Type.CATALOG)
public Response createCatalog(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
CatalogCreateRequest request) {
LOG.info("Received create catalog request for metalake: {}", metalake);
@@ -184,7 +184,7 @@ public class CatalogOperations {
@Timed(name = "test-connection." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "test-connection", absolute = true)
public Response testConnection(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
CatalogCreateRequest request) {
LOG.info("Received test connection request for catalog: {}.{}", metalake,
request.getName());
@@ -221,9 +221,9 @@ public class CatalogOperations {
expression = "ANY_USE_CATALOG || ANY(OWNER, METALAKE, CATALOG)",
accessMetadataType = MetadataObject.Type.CATALOG)
public Response setCatalog(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG)
String catalogName,
CatalogSetRequest request) {
LOG.info("Received set request for catalog: {}.{}", metalake, catalogName);
@@ -267,9 +267,9 @@ public class CatalogOperations {
expression = "ANY_USE_CATALOG || ANY(OWNER, METALAKE, CATALOG)",
accessMetadataType = MetadataObject.Type.CATALOG)
public Response loadCatalog(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalakeName,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG)
String catalogName) {
LOG.info("Received load catalog request for catalog: {}.{}", metalakeName,
catalogName);
try {
@@ -294,9 +294,9 @@ public class CatalogOperations {
expression = "ANY(OWNER, METALAKE, CATALOG)",
accessMetadataType = MetadataObject.Type.CATALOG)
public Response alterCatalog(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalakeName,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG)
String catalogName,
CatalogUpdatesRequest request) {
LOG.info("Received alter catalog request for catalog: {}.{}",
metalakeName, catalogName);
@@ -331,9 +331,9 @@ public class CatalogOperations {
expression = "ANY(OWNER, METALAKE, CATALOG)",
accessMetadataType = MetadataObject.Type.CATALOG)
public Response dropCatalog(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalakeName,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG)
String catalogName,
@DefaultValue("false") @QueryParam("force") boolean force) {
LOG.info("Received drop catalog request for catalog: {}.{}", metalakeName,
catalogName);
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 94a4b03bde..1d06e82ea2 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
@@ -98,12 +98,10 @@ public class FilesetOperations {
@Timed(name = "list-fileset." + MetricNames.HTTP_PROCESS_DURATION, absolute
= true)
@ResponseMetered(name = "list-fileset", absolute = true)
public Response listFilesets(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA)
- String schema) {
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema) {
try {
LOG.info("Received list filesets request for schema: {}.{}.{}",
metalake, catalog, schema);
@@ -144,11 +142,10 @@ public class FilesetOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && SCHEMA::CREATE_FILESET",
accessMetadataType = MetadataObject.Type.FILESET)
public Response createFileset(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
FilesetCreateRequest request) {
LOG.info(
"Received create fileset request: {}.{}.{}.{}",
@@ -200,12 +197,11 @@ public class FilesetOperations {
expression = loadFilesetAuthorizationExpression,
accessMetadataType = MetadataObject.Type.FILESET)
public Response loadFileset(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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)
+ @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) {
LOG.info("Received load fileset request: {}.{}.{}.{}", metalake, catalog,
schema, fileset);
try {
@@ -285,13 +281,11 @@ public class FilesetOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && (FILESET::OWNER ||
ANY_WRITE_FILESET)",
accessMetadataType = MetadataObject.Type.FILESET)
public Response alterFileset(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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,
+ @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,
FilesetUpdatesRequest request) {
LOG.info("Received alter fileset request: {}.{}.{}.{}", metalake, catalog,
schema, fileset);
try {
@@ -327,12 +321,11 @@ public class FilesetOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && FILESET::OWNER",
accessMetadataType = MetadataObject.Type.FILESET)
public Response dropFileset(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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)
+ @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) {
LOG.info("Received drop fileset request: {}.{}.{}.{}", metalake, catalog,
schema, fileset);
try {
@@ -364,13 +357,11 @@ public class FilesetOperations {
expression = loadFilesetAuthorizationExpression,
accessMetadataType = MetadataObject.Type.FILESET)
public Response getFileLocation(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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,
+ @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") @NotNull String subPath,
@QueryParam("location_name") String locationName) {
LOG.info(
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/GroupOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/GroupOperations.java
index e157edc46a..00498ba8eb 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/GroupOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/GroupOperations.java
@@ -31,6 +31,7 @@ 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.GravitinoEnv;
import org.apache.gravitino.Namespace;
import org.apache.gravitino.authorization.AccessControlDispatcher;
@@ -42,6 +43,8 @@ import org.apache.gravitino.dto.responses.RemoveResponse;
import org.apache.gravitino.dto.util.DTOConverters;
import org.apache.gravitino.metrics.MetricNames;
import org.apache.gravitino.server.authorization.NameBindings;
+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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -86,7 +89,11 @@ public class GroupOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "add-group." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "add-group", absolute = true)
- public Response addGroup(@PathParam("metalake") String metalake,
GroupAddRequest request) {
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::MANAGE_GROUPS")
+ public Response addGroup(
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ GroupAddRequest request) {
try {
return Utils.doAs(
httpRequest,
@@ -108,8 +115,11 @@ public class GroupOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "remove-group." + MetricNames.HTTP_PROCESS_DURATION, absolute
= true)
@ResponseMetered(name = "remove-group", absolute = true)
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::MANAGE_GROUPS")
public Response removeGroup(
- @PathParam("metalake") String metalake, @PathParam("group") String
group) {
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("group") String group) {
try {
return Utils.doAs(
httpRequest,
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
index e4f2568d10..ae579259ac 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetalakeOperations.java
@@ -39,7 +39,6 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.gravitino.Entity;
-import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.Metalake;
import org.apache.gravitino.MetalakeChange;
import org.apache.gravitino.NameIdentifier;
@@ -157,7 +156,7 @@ public class MetalakeOperations {
@ResponseMetered(name = "load-metalake", absolute = true)
@AuthorizationExpression(expression = "METALAKE_USER")
public Response loadMetalake(
- @PathParam("name") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("name") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalakeName) {
LOG.info("Received load metalake request for metalake: {}", metalakeName);
try {
@@ -183,7 +182,7 @@ public class MetalakeOperations {
@ResponseMetered(name = "set-metalake", absolute = true)
@AuthorizationExpression(expression = "METALAKE::OWNER")
public Response setMetalake(
- @PathParam("name") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("name") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalakeName,
MetalakeSetRequest request) {
LOG.info("Received set request for metalake: {}", metalakeName);
@@ -218,7 +217,7 @@ public class MetalakeOperations {
@ResponseMetered(name = "alter-metalake", absolute = true)
@AuthorizationExpression(expression = "METALAKE::OWNER")
public Response alterMetalake(
- @PathParam("name") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("name") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalakeName,
MetalakeUpdatesRequest updatesRequest) {
LOG.info("Received alter metalake request for metalake: {}", metalakeName);
@@ -251,7 +250,7 @@ public class MetalakeOperations {
@ResponseMetered(name = "drop-metalake", absolute = true)
@AuthorizationExpression(expression = "METALAKE::OWNER")
public Response dropMetalake(
- @PathParam("name") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("name") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalakeName,
@DefaultValue("false") @QueryParam("force") boolean force) {
LOG.info("Received drop metalake request for metalake: {}", metalakeName);
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 b5c5ff7e66..0ad3875922 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
@@ -124,12 +124,11 @@ public class ModelOperations {
expression = loadModelAuthorizationExpression,
accessMetadataType = MetadataObject.Type.MODEL)
public Response getModel(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model) {
LOG.info("Received get model request: {}.{}.{}.{}", metalake, catalog,
schema, model);
NameIdentifier modelId = NameIdentifierUtil.ofModel(metalake, catalog,
schema, model);
@@ -158,11 +157,10 @@ public class ModelOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && ANY_CREATE_MODEL",
accessMetadataType = MetadataObject.Type.SCHEMA)
public Response registerModel(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
ModelRegisterRequest request) {
LOG.info(
"Received register model request: {}.{}.{}.{}",
@@ -204,12 +202,11 @@ public class ModelOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
accessMetadataType = MetadataObject.Type.MODEL)
public Response deleteModel(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model) {
LOG.info("Received delete model request: {}.{}.{}.{}", metalake, catalog,
schema, model);
NameIdentifier modelId = NameIdentifierUtil.ofModel(metalake, catalog,
schema, model);
@@ -314,12 +311,11 @@ public class ModelOperations {
expression = loadModelAuthorizationExpression,
accessMetadataType = MetadataObject.Type.MODEL)
public Response getModelVersion(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model,
@PathParam("version") int version) {
LOG.info(
"Received get model version request: {}.{}.{}.{}.{}",
@@ -354,12 +350,11 @@ public class ModelOperations {
expression = loadModelAuthorizationExpression,
accessMetadataType = MetadataObject.Type.MODEL)
public Response getModelVersionByAlias(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model,
@PathParam("alias") String alias) {
LOG.info(
"Received get model version alias request: {}.{}.{}.{}.{}",
@@ -397,12 +392,11 @@ public class ModelOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && (MODEL::OWNER ||
ANY_USE_MODEL && ANY_CREATE_MODEL_VERSION)",
accessMetadataType = MetadataObject.Type.MODEL)
public Response linkModelVersion(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model,
ModelVersionLinkRequest request) {
LOG.info("Received link model version request: {}.{}.{}.{}", metalake,
catalog, schema, model);
NameIdentifier modelId = NameIdentifierUtil.ofModel(metalake, catalog,
schema, model);
@@ -440,12 +434,11 @@ public class ModelOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
accessMetadataType = MetadataObject.Type.MODEL)
public Response deleteModelVersion(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model,
@PathParam("version") int version) {
LOG.info(
"Received delete model version request: {}.{}.{}.{}.{}",
@@ -488,12 +481,11 @@ public class ModelOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
accessMetadataType = MetadataObject.Type.MODEL)
public Response deleteModelVersionByAlias(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model,
@PathParam("alias") String alias) {
LOG.info(
"Received delete model version by alias request: {}.{}.{}.{}.{}",
@@ -537,12 +529,11 @@ public class ModelOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
accessMetadataType = MetadataObject.Type.MODEL)
public Response alterModelVersion(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model,
@PathParam("version") int version,
ModelVersionUpdatesRequest request) {
LOG.info(
@@ -591,12 +582,11 @@ public class ModelOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
accessMetadataType = MetadataObject.Type.MODEL)
public Response alterModelVersionByAlias(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model,
@PathParam("alias") String alias,
ModelVersionUpdatesRequest request) {
LOG.info(
@@ -644,12 +634,11 @@ public class ModelOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && MODEL::OWNER",
accessMetadataType = MetadataObject.Type.MODEL)
public Response alterModel(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.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("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("model") @AuthorizationMetadata(type =
Entity.EntityType.MODEL) String model,
ModelUpdatesRequest request) {
LOG.info("Received alter model request: {}.{}.{}.{}", metalake, catalog,
schema, model);
try {
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/OwnerOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/OwnerOperations.java
index 89d0d93a36..67fe25ff21 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/OwnerOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/OwnerOperations.java
@@ -18,6 +18,8 @@
*/
package org.apache.gravitino.server.web.rest;
+import static
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConverter.CAN_SET_OWNER;
+
import com.codahale.metrics.annotation.ResponseMetered;
import com.codahale.metrics.annotation.Timed;
import java.util.Locale;
@@ -41,6 +43,7 @@ import org.apache.gravitino.dto.responses.SetResponse;
import org.apache.gravitino.dto.util.DTOConverters;
import org.apache.gravitino.metrics.MetricNames;
import org.apache.gravitino.server.authorization.NameBindings;
+import
org.apache.gravitino.server.authorization.annotations.AuthorizationExpression;
import org.apache.gravitino.server.web.Utils;
import org.apache.gravitino.utils.MetadataObjectUtil;
@@ -94,6 +97,7 @@ public class OwnerOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "set-object-owner." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "set-object-owner", absolute = true)
+ @AuthorizationExpression(expression = CAN_SET_OWNER)
public Response setOwnerForObject(
@PathParam("metalake") String metalake,
@PathParam("metadataObjectType") String metadataObjectType,
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java
index 8ef902a994..a78c6e9976 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/PermissionOperations.java
@@ -18,6 +18,8 @@
*/
package org.apache.gravitino.server.web.rest;
+import static
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConverter.CAN_OPERATE_METADATA_PRIVILEGE;
+
import com.codahale.metrics.annotation.ResponseMetered;
import com.codahale.metrics.annotation.Timed;
import java.util.Locale;
@@ -30,6 +32,7 @@ import javax.ws.rs.Produces;
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.GravitinoEnv;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.MetadataObjects;
@@ -46,6 +49,8 @@ import org.apache.gravitino.dto.responses.UserResponse;
import org.apache.gravitino.dto.util.DTOConverters;
import org.apache.gravitino.metrics.MetricNames;
import org.apache.gravitino.server.authorization.NameBindings;
+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.MetadataObjectUtil;
@@ -69,8 +74,10 @@ public class PermissionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "grant-roles-to-user." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "grant-roles-to-user", absolute = true)
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::MANAGE_GRANTS")
public Response grantRolesToUser(
- @PathParam("metalake") String metalake,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
@PathParam("user") String user,
RoleGrantRequest request) {
try {
@@ -95,8 +102,10 @@ public class PermissionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "grant-roles-to-group." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "grant-roles-to-group", absolute = true)
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::MANAGE_GRANTS")
public Response grantRolesToGroup(
- @PathParam("metalake") String metalake,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
@PathParam("group") String group,
RoleGrantRequest request) {
try {
@@ -121,8 +130,10 @@ public class PermissionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "revoke-roles-from-user." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "revoke-roles-from-user", absolute = true)
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::MANAGE_GRANTS")
public Response revokeRolesFromUser(
- @PathParam("metalake") String metalake,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
@PathParam("user") String user,
RoleRevokeRequest request) {
try {
@@ -147,8 +158,10 @@ public class PermissionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "revoke-roles-from-group." +
MetricNames.HTTP_PROCESS_DURATION, absolute = true)
@ResponseMetered(name = "revokes-role-from-group", absolute = true)
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::MANAGE_GRANTS")
public Response revokeRolesFromGroup(
- @PathParam("metalake") String metalake,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
@PathParam("group") String group,
RoleRevokeRequest request) {
try {
@@ -173,8 +186,12 @@ public class PermissionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "grant-privilege-to-role." +
MetricNames.HTTP_PROCESS_DURATION, absolute = true)
@ResponseMetered(name = "grant-privilege-to-role", absolute = true)
+ @AuthorizationExpression(
+ expression = CAN_OPERATE_METADATA_PRIVILEGE,
+ errorMessage = "Current user can not grant privilege to role.")
public Response grantPrivilegeToRole(
- @PathParam("metalake") String metalake,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
@PathParam("role") String role,
@PathParam("type") String type,
@PathParam("fullName") String fullName,
@@ -216,8 +233,12 @@ public class PermissionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "revoke-privilege-from-role." +
MetricNames.HTTP_PROCESS_DURATION, absolute = true)
@ResponseMetered(name = "revoke-privilege-from-role", absolute = true)
+ @AuthorizationExpression(
+ expression = CAN_OPERATE_METADATA_PRIVILEGE,
+ errorMessage = "Current user can not revoke privilege from role.")
public Response revokePrivilegeFromRole(
- @PathParam("metalake") String metalake,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
@PathParam("role") String role,
@PathParam("type") String type,
@PathParam("fullName") String fullName,
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/RoleOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/RoleOperations.java
index 844547853f..c54a6be497 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/RoleOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/RoleOperations.java
@@ -34,9 +34,11 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
+import org.apache.gravitino.Entity;
import org.apache.gravitino.GravitinoEnv;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.authorization.AccessControlDispatcher;
import org.apache.gravitino.authorization.AuthorizationUtils;
import org.apache.gravitino.authorization.Privilege;
@@ -52,9 +54,13 @@ import org.apache.gravitino.dto.util.DTOConverters;
import org.apache.gravitino.exceptions.IllegalMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.metrics.MetricNames;
+import org.apache.gravitino.server.authorization.MetadataFilterHelper;
import org.apache.gravitino.server.authorization.NameBindings;
+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.MetadataObjectUtil;
+import org.apache.gravitino.utils.NameIdentifierUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -81,6 +87,22 @@ public class RoleOperations {
httpRequest,
() -> {
String[] names = accessControlManager.listRoleNames(metalake);
+ names =
+ Arrays.stream(names)
+ .filter(
+ role -> {
+ NameIdentifier[] nameIdentifiers =
+ new NameIdentifier[]
{NameIdentifierUtil.ofRole(metalake, role)};
+ return MetadataFilterHelper.filterByExpression(
+ metalake,
+ "METALAKE::OWNER || ROLE::OWNER ||
ROLE::SELF",
+ Entity.EntityType.ROLE,
+ nameIdentifiers)
+ .length
+ > 0;
+ })
+ .collect(Collectors.toList())
+ .toArray(new String[0]);
return Utils.ok(new NameListResponse(names));
});
} catch (Exception e) {
@@ -93,7 +115,11 @@ public class RoleOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "get-role." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "get-role", absolute = true)
- public Response getRole(@PathParam("metalake") String metalake,
@PathParam("role") String role) {
+ @AuthorizationExpression(expression = "METALAKE::OWNER || ROLE::OWNER ||
ROLE::SELF")
+ public Response getRole(
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("role") @AuthorizationMetadata(type = Entity.EntityType.ROLE)
String role) {
try {
return Utils.doAs(
httpRequest,
@@ -110,7 +136,11 @@ public class RoleOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "create-role." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "create-role", absolute = true)
- public Response createRole(@PathParam("metalake") String metalake,
RoleCreateRequest request) {
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::CREATE_ROLE")
+ public Response createRole(
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ RoleCreateRequest request) {
try {
return Utils.doAs(
@@ -177,8 +207,11 @@ public class RoleOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "delete-role." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "delete-role", absolute = true)
+ @AuthorizationExpression(expression = "METALAKE::OWNER || ROLE::OWNER")
public Response deleteRole(
- @PathParam("metalake") String metalake, @PathParam("role") String role) {
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("role") @AuthorizationMetadata(type = Entity.EntityType.ROLE)
String role) {
try {
return Utils.doAs(
httpRequest,
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/SchemaOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/SchemaOperations.java
index 939ef8a726..fe3e909cfd 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/SchemaOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/SchemaOperations.java
@@ -112,10 +112,9 @@ public class SchemaOperations {
expression = "ANY(OWNER, METALAKE, CATALOG) || ANY_USE_CATALOG &&
ANY_CREATE_SCHEMA",
accessMetadataType = MetadataObject.Type.CATALOG)
public Response createSchema(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
SchemaCreateRequest request) {
LOG.info("Received create schema request: {}.{}.{}", metalake, catalog,
request.getName());
try {
@@ -147,12 +146,10 @@ public class SchemaOperations {
expression = loadSchemaAuthorizationExpression,
accessMetadataType = MetadataObject.Type.SCHEMA)
public Response loadSchema(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA)
- String schema) {
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema) {
LOG.info("Received load schema request for schema: {}.{}.{}", metalake,
catalog, schema);
try {
return Utils.doAs(
@@ -179,11 +176,10 @@ public class SchemaOperations {
expression = "ANY(OWNER, METALAKE, CATALOG) ||
SCHEMA_OWNER_WITH_USE_CATALOG",
accessMetadataType = MetadataObject.Type.SCHEMA)
public Response alterSchema(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
SchemaUpdatesRequest request) {
LOG.info("Received alter schema request: {}.{}.{}", metalake, catalog,
schema);
try {
@@ -216,11 +212,10 @@ public class SchemaOperations {
expression = "ANY(OWNER, METALAKE, CATALOG) ||
SCHEMA_OWNER_WITH_USE_CATALOG",
accessMetadataType = MetadataObject.Type.SCHEMA)
public Response dropSchema(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
@DefaultValue("false") @QueryParam("cascade") boolean cascade) {
LOG.info("Received drop schema request: {}.{}.{}", metalake, catalog,
schema);
try {
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
index e59756625b..3b20fe6d02 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
@@ -119,11 +119,10 @@ public class TableOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && ANY_CREATE_TABLE",
accessMetadataType = MetadataObject.Type.TABLE)
public Response createTable(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
TableCreateRequest request) {
LOG.info(
"Received create table request: {}.{}.{}.{}", metalake, catalog,
schema, request.getName());
@@ -168,12 +167,11 @@ public class TableOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER ||
ANY_SELECT_TABLE|| ANY_MODIFY_TABLE)",
accessMetadataType = MetadataObject.Type.TABLE)
public Response loadTable(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
- @PathParam("table") @AuthorizationMetadata(type =
MetadataObject.Type.TABLE) String table) {
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("table") @AuthorizationMetadata(type =
Entity.EntityType.TABLE) String table) {
LOG.info(
"Received load table request for table: {}.{}.{}.{}", metalake,
catalog, schema, table);
try {
@@ -203,12 +201,11 @@ public class TableOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER ||
ANY_MODIFY_TABLE)",
accessMetadataType = MetadataObject.Type.TABLE)
public Response alterTable(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
- @PathParam("table") @AuthorizationMetadata(type =
MetadataObject.Type.TABLE) String table,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("table") @AuthorizationMetadata(type =
Entity.EntityType.TABLE) String table,
TableUpdatesRequest request) {
LOG.info("Received alter table request: {}.{}.{}.{}", metalake, catalog,
schema, table);
try {
@@ -244,12 +241,11 @@ public class TableOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && TABLE::OWNER ",
accessMetadataType = MetadataObject.Type.TABLE)
public Response dropTable(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
- @PathParam("table") @AuthorizationMetadata(type =
MetadataObject.Type.TABLE) String table,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("table") @AuthorizationMetadata(type =
Entity.EntityType.TABLE) String table,
@QueryParam("purge") @DefaultValue("false") boolean purge) {
LOG.info(
"Received {} table request: {}.{}.{}.{}",
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/TopicOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/TopicOperations.java
index c3dd0e4c62..7712316ece 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/TopicOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/TopicOperations.java
@@ -114,11 +114,10 @@ public class TopicOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && ANY_CREATE_TOPIC",
accessMetadataType = MetadataObject.Type.SCHEMA)
public Response createTopic(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
TopicCreateRequest request) {
LOG.info("Received create topic request: {}.{}.{}", metalake, catalog,
schema);
try {
@@ -160,12 +159,11 @@ public class TopicOperations {
expression = loadTopicsAuthorizationExpression,
accessMetadataType = MetadataObject.Type.TOPIC)
public Response loadTopic(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
- @PathParam("topic") @AuthorizationMetadata(type =
MetadataObject.Type.TOPIC) String topic) {
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("topic") @AuthorizationMetadata(type =
Entity.EntityType.TOPIC) String topic) {
LOG.info(
"Received load topic request for topic: {}.{}.{}.{}", metalake,
catalog, schema, topic);
try {
@@ -196,12 +194,11 @@ public class TopicOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && (TOPIC::OWNER ||
ANY_PRODUCE_TOPIC)",
accessMetadataType = MetadataObject.Type.TOPIC)
public Response alterTopic(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
- @PathParam("topic") @AuthorizationMetadata(type =
MetadataObject.Type.TOPIC) String topic,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("topic") @AuthorizationMetadata(type =
Entity.EntityType.TOPIC) String topic,
TopicUpdatesRequest request) {
LOG.info("Received alter topic request: {}.{}.{}.{}", metalake, catalog,
schema, topic);
try {
@@ -238,12 +235,11 @@ public class TopicOperations {
+ "ANY_USE_CATALOG && ANY_USE_SCHEMA && TOPIC::OWNER",
accessMetadataType = MetadataObject.Type.TOPIC)
public Response dropTopic(
- @PathParam("metalake") @AuthorizationMetadata(type =
MetadataObject.Type.METALAKE)
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
String metalake,
- @PathParam("catalog") @AuthorizationMetadata(type =
MetadataObject.Type.CATALOG)
- String catalog,
- @PathParam("schema") @AuthorizationMetadata(type =
MetadataObject.Type.SCHEMA) String schema,
- @PathParam("topic") @AuthorizationMetadata(type =
MetadataObject.Type.TOPIC) String topic) {
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("topic") @AuthorizationMetadata(type =
Entity.EntityType.TOPIC) String topic) {
LOG.info("Received drop topic request: {}.{}.{}.{}", metalake, catalog,
schema, topic);
try {
return Utils.doAs(
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/UserOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/UserOperations.java
index 48147368ea..b25b91a704 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/UserOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/UserOperations.java
@@ -20,6 +20,8 @@ package org.apache.gravitino.server.web.rest;
import com.codahale.metrics.annotation.ResponseMetered;
import com.codahale.metrics.annotation.Timed;
+import java.util.Arrays;
+import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
@@ -31,8 +33,11 @@ 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.GravitinoEnv;
+import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.authorization.AccessControlDispatcher;
+import org.apache.gravitino.authorization.User;
import org.apache.gravitino.dto.requests.UserAddRequest;
import org.apache.gravitino.dto.responses.NameListResponse;
import org.apache.gravitino.dto.responses.RemoveResponse;
@@ -40,8 +45,12 @@ import org.apache.gravitino.dto.responses.UserListResponse;
import org.apache.gravitino.dto.responses.UserResponse;
import org.apache.gravitino.dto.util.DTOConverters;
import org.apache.gravitino.metrics.MetricNames;
+import org.apache.gravitino.server.authorization.MetadataFilterHelper;
import org.apache.gravitino.server.authorization.NameBindings;
+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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,7 +76,11 @@ public class UserOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "get-user." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "get-user", absolute = true)
- public Response getUser(@PathParam("metalake") String metalake,
@PathParam("user") String user) {
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
MATALAKE::MANAGE_USERS || USER::SELF")
+ public Response getUser(
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("user") @AuthorizationMetadata(type = Entity.EntityType.USER)
String user) {
try {
return Utils.doAs(
httpRequest,
@@ -92,9 +105,26 @@ public class UserOperations {
httpRequest,
() -> {
if (verbose) {
- return Utils.ok(
- new UserListResponse(
-
DTOConverters.toDTOs(accessControlManager.listUsers(metalake))));
+ User[] users = accessControlManager.listUsers(metalake);
+ users =
+ Arrays.stream(users)
+ .filter(
+ user -> {
+ NameIdentifier[] nameIdentifiers =
+ new NameIdentifier[] {
+ NameIdentifierUtil.ofUser(metalake,
user.name())
+ };
+ return MetadataFilterHelper.filterByExpression(
+ metalake,
+ "METALAKE::OWNER ||
MATALAKE::MANAGE_USERS || USER::SELF",
+ Entity.EntityType.USER,
+ nameIdentifiers)
+ .length
+ > 0;
+ })
+ .collect(Collectors.toList())
+ .toArray(new User[0]);
+ return Utils.ok(new
UserListResponse(DTOConverters.toDTOs(users)));
} else {
return Utils.ok(new
NameListResponse(accessControlManager.listUserNames(metalake)));
}
@@ -108,7 +138,11 @@ public class UserOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "add-user." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "add-user", absolute = true)
- public Response addUser(@PathParam("metalake") String metalake,
UserAddRequest request) {
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::MANAGE_USERS")
+ public Response addUser(
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ UserAddRequest request) {
try {
return Utils.doAs(
httpRequest,
@@ -130,8 +164,11 @@ public class UserOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "remove-user." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "remove-user", absolute = true)
+ @AuthorizationExpression(expression = "METALAKE::OWNER ||
METALAKE::MANAGE_USERS")
public Response removeUser(
- @PathParam("metalake") String metalake, @PathParam("user") String user) {
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("user") String user) {
try {
return Utils.doAs(
httpRequest,
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
b/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
index 48ad9c0eaf..02c2cc0477 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/filter/TestGravitinoInterceptionService.java
@@ -29,7 +29,9 @@ import java.util.List;
import javax.ws.rs.core.Response;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
+import org.apache.gravitino.Entity;
import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.UserPrincipal;
import org.apache.gravitino.authorization.GravitinoAuthorizer;
import org.apache.gravitino.authorization.Privilege;
@@ -130,7 +132,7 @@ public class TestGravitinoInterceptionService {
expression = "METALAKE::USE_CATALOG || METALAKE::OWNER",
accessMetadataType = MetadataObject.Type.METALAKE)
public Response testMethod(
- @AuthorizationMetadata(type = MetadataObject.Type.METALAKE) String
metalake) {
+ @AuthorizationMetadata(type = Entity.EntityType.METALAKE) String
metalake) {
return Utils.ok("ok");
}
}
@@ -162,9 +164,33 @@ public class TestGravitinoInterceptionService {
return false;
}
+ @Override
+ public boolean isSelf(Entity.EntityType type, NameIdentifier
nameIdentifier) {
+ return true;
+ }
+
+ @Override
+ public boolean isMetalakeUser(String metalake) {
+ return true;
+ }
+
+ @Override
+ public boolean hasSetOwnerPermission(String metalake, String type, String
fullName) {
+ return true;
+ }
+
+ @Override
+ public boolean hasMetadataPrivilegePermission(String metalake, String
type, String fullName) {
+ return true;
+ }
+
@Override
public void handleRolePrivilegeChange(Long roleId) {}
+ @Override
+ public void handleMetadataOwnerChange(
+ String metalake, Long oldOwnerId, NameIdentifier nameIdentifier,
Entity.EntityType type) {}
+
@Override
public void close() throws IOException {}
}
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestGroupOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestGroupOperations.java
index ac4f8c66a8..d4b782da7d 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestGroupOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestGroupOperations.java
@@ -57,14 +57,13 @@ import org.apache.gravitino.meta.GroupEntity;
import org.apache.gravitino.rest.RESTUtils;
import org.glassfish.hk2.utilities.binding.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;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
-public class TestGroupOperations extends JerseyTest {
+public class TestGroupOperations extends BaseOperationsTest {
private static final AccessControlManager manager =
mock(AccessControlManager.class);
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestRoleOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestRoleOperations.java
index 06d9fcc27e..708c8603cf 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestRoleOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestRoleOperations.java
@@ -78,14 +78,13 @@ import org.apache.gravitino.rest.RESTUtils;
import org.apache.gravitino.utils.MetadataObjectUtil;
import org.glassfish.hk2.utilities.binding.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;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
-public class TestRoleOperations extends JerseyTest {
+public class TestRoleOperations extends BaseOperationsTest {
private static final AccessControlManager manager =
mock(AccessControlManager.class);
private static final MetalakeDispatcher metalakeDispatcher =
mock(MetalakeDispatcher.class);
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestUserOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestUserOperations.java
index 82bc59155b..2fa85b0213 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestUserOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestUserOperations.java
@@ -56,14 +56,13 @@ import org.apache.gravitino.meta.UserEntity;
import org.apache.gravitino.rest.RESTUtils;
import org.glassfish.hk2.utilities.binding.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;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
-public class TestUserOperations extends JerseyTest {
+public class TestUserOperations extends BaseOperationsTest {
private static final AccessControlManager manager =
mock(AccessControlManager.class);