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);
 


Reply via email to