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

jshao pushed a commit to branch branch-0.6
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-0.6 by this push:
     new dd981346e [#3346] feat(core,server): Supports to list roles operations 
(#5022)
dd981346e is described below

commit dd981346e5d014beeb3722a8f92a4491796325fb
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Sep 26 10:24:39 2024 +0800

    [#3346] feat(core,server): Supports to list roles operations (#5022)
    
    ### What changes were proposed in this pull request?
    Supports to list roles operations
    
    ### Why are the changes needed?
    
    Fix: #3346
    
    ### Does this PR introduce _any_ user-facing change?
    Yes, will add the document later.
    
    ### How was this patch tested?
    Add UTs.
    
    Co-authored-by: roryqi <[email protected]>
---
 .../org/apache/gravitino/authorization/Role.java   |  3 +-
 .../apache/gravitino/client/GravitinoClient.java   | 10 +++
 .../apache/gravitino/client/GravitinoMetalake.java | 30 +++++--
 .../java/org/apache/gravitino/client/TestRole.java | 24 ++++++
 .../gravitino/dto/authorization/RoleDTO.java       |  6 +-
 .../gravitino/dto/responses/RoleResponse.java      |  3 +-
 .../java/org/apache/gravitino/EntityStore.java     | 13 ++-
 .../authorization/AccessControlDispatcher.java     | 11 ++-
 .../authorization/AccessControlManager.java        |  5 ++
 .../gravitino/authorization/RoleManager.java       | 21 ++++-
 .../gravitino/authorization/UserGroupManager.java  | 14 +---
 .../hook/AccessControlHookDispatcher.java          |  5 ++
 .../java/org/apache/gravitino/meta/RoleEntity.java | 15 +---
 .../java/org/apache/gravitino/meta/UserEntity.java | 18 -----
 .../gravitino/storage/relational/JDBCBackend.java  | 10 +--
 .../storage/relational/RelationalBackend.java      | 11 ++-
 .../storage/relational/RelationalEntityStore.java  |  9 +--
 .../storage/relational/mapper/RoleMetaMapper.java  |  3 +
 .../mapper/RoleMetaSQLProviderFactory.java         |  4 +
 .../provider/base/RoleMetaBaseSQLProvider.java     | 15 ++++
 .../relational/service/RoleMetaService.java        | 18 +++++
 .../relational/service/SupportsDesiredFields.java  | 40 ---------
 .../service/SupportsDesiredFieldsHandlers.java     | 50 ------------
 .../relational/service/UserMetaService.java        | 94 ++++++----------------
 .../authorization/TestAccessControlManager.java    | 26 ++++++
 .../java/org/apache/gravitino/meta/TestEntity.java |  2 +-
 .../storage/relational/TestJDBCBackend.java        | 19 ++---
 .../relational/service/TestRoleMetaService.java    | 49 +++++++++++
 .../relational/service/TestUserMetaService.java    |  3 +-
 .../test/authorization/AccessControlIT.java        | 28 +++++++
 .../gravitino/server/web/rest/RoleOperations.java  | 22 +++++
 .../server/web/rest/TestRoleOperations.java        | 48 +++++++++++
 32 files changed, 374 insertions(+), 255 deletions(-)

diff --git a/api/src/main/java/org/apache/gravitino/authorization/Role.java 
b/api/src/main/java/org/apache/gravitino/authorization/Role.java
index c7441a920..9b8453f80 100644
--- a/api/src/main/java/org/apache/gravitino/authorization/Role.java
+++ b/api/src/main/java/org/apache/gravitino/authorization/Role.java
@@ -25,8 +25,7 @@ import org.apache.gravitino.annotation.Evolving;
 
 /**
  * The interface of a role. The role is the entity which has kinds of 
privileges. One role can have
- * multiple privileges of one securable object. Gravitino chooses to bind one 
securable object to
- * one role to avoid granting too many privileges to one role.
+ * multiple privileges of multiple securable objects.
  */
 @Evolving
 public interface Role extends Auditable {
diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClient.java
 
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClient.java
index e074770e8..a7656fd02 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClient.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoClient.java
@@ -359,6 +359,16 @@ public class GravitinoClient extends GravitinoClientBase
     getMetalake().setOwner(object, ownerName, ownerType);
   }
 
+  /**
+   * Lists the role names.
+   *
+   * @return The role name list.
+   * @throws NoSuchMetalakeException If the Metalake with the given name does 
not exist.
+   */
+  public String[] listRoleNames() throws NoSuchMetalakeException {
+    return getMetalake().listRoleNames();
+  }
+
   /**
    * Creates a new builder for constructing a GravitinoClient.
    *
diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoMetalake.java
 
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoMetalake.java
index 9a13a9dd1..58973b4cf 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoMetalake.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoMetalake.java
@@ -100,9 +100,9 @@ public class GravitinoMetalake extends MetalakeDTO 
implements SupportsCatalogs,
   private static final String API_METALAKES_GROUPS_PATH = 
"api/metalakes/%s/groups/%s";
   private static final String API_METALAKES_ROLES_PATH = 
"api/metalakes/%s/roles/%s";
   private static final String API_METALAKES_OWNERS_PATH = 
"api/metalakes/%s/owners/%s";
-  private static final String BLANK_PLACE_HOLDER = "";
 
   private static final String API_METALAKES_TAGS_PATH = 
"api/metalakes/%s/tags";
+  private static final String BLANK_PLACEHOLDER = "";
 
   private final RESTClient restClient;
 
@@ -464,7 +464,7 @@ public class GravitinoMetalake extends MetalakeDTO 
implements SupportsCatalogs,
 
     UserResponse resp =
         restClient.post(
-            String.format(API_METALAKES_USERS_PATH, this.name(), 
BLANK_PLACE_HOLDER),
+            String.format(API_METALAKES_USERS_PATH, this.name(), 
BLANK_PLACEHOLDER),
             req,
             UserResponse.class,
             Collections.emptyMap(),
@@ -528,7 +528,7 @@ public class GravitinoMetalake extends MetalakeDTO 
implements SupportsCatalogs,
 
     UserListResponse resp =
         restClient.get(
-            String.format(API_METALAKES_USERS_PATH, name(), 
BLANK_PLACE_HOLDER),
+            String.format(API_METALAKES_USERS_PATH, name(), BLANK_PLACEHOLDER),
             params,
             UserListResponse.class,
             Collections.emptyMap(),
@@ -547,7 +547,7 @@ public class GravitinoMetalake extends MetalakeDTO 
implements SupportsCatalogs,
   public String[] listUserNames() throws NoSuchMetalakeException {
     NameListResponse resp =
         restClient.get(
-            String.format(API_METALAKES_USERS_PATH, name(), 
BLANK_PLACE_HOLDER),
+            String.format(API_METALAKES_USERS_PATH, name(), BLANK_PLACEHOLDER),
             NameListResponse.class,
             Collections.emptyMap(),
             ErrorHandlers.userErrorHandler());
@@ -571,7 +571,7 @@ public class GravitinoMetalake extends MetalakeDTO 
implements SupportsCatalogs,
 
     GroupResponse resp =
         restClient.post(
-            String.format(API_METALAKES_GROUPS_PATH, this.name(), 
BLANK_PLACE_HOLDER),
+            String.format(API_METALAKES_GROUPS_PATH, this.name(), 
BLANK_PLACEHOLDER),
             req,
             GroupResponse.class,
             Collections.emptyMap(),
@@ -691,7 +691,7 @@ public class GravitinoMetalake extends MetalakeDTO 
implements SupportsCatalogs,
 
     RoleResponse resp =
         restClient.post(
-            String.format(API_METALAKES_ROLES_PATH, this.name(), 
BLANK_PLACE_HOLDER),
+            String.format(API_METALAKES_ROLES_PATH, this.name(), 
BLANK_PLACEHOLDER),
             req,
             RoleResponse.class,
             Collections.emptyMap(),
@@ -701,6 +701,24 @@ public class GravitinoMetalake extends MetalakeDTO 
implements SupportsCatalogs,
     return resp.getRole();
   }
 
+  /**
+   * Lists the role names.
+   *
+   * @return The role name list.
+   * @throws NoSuchMetalakeException If the Metalake with the given name does 
not exist.
+   */
+  public String[] listRoleNames() {
+    NameListResponse resp =
+        restClient.get(
+            String.format(API_METALAKES_ROLES_PATH, this.name(), 
BLANK_PLACEHOLDER),
+            NameListResponse.class,
+            Collections.emptyMap(),
+            ErrorHandlers.roleErrorHandler());
+    resp.validate();
+
+    return resp.getNames();
+  }
+
   /**
    * Grant roles to a user.
    *
diff --git 
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestRole.java 
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestRole.java
index 370f1941d..3d0771fc5 100644
--- 
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestRole.java
+++ 
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestRole.java
@@ -40,6 +40,7 @@ import org.apache.gravitino.dto.requests.RoleCreateRequest;
 import org.apache.gravitino.dto.responses.DeleteResponse;
 import org.apache.gravitino.dto.responses.ErrorResponse;
 import org.apache.gravitino.dto.responses.MetalakeResponse;
+import org.apache.gravitino.dto.responses.NameListResponse;
 import org.apache.gravitino.dto.responses.RoleResponse;
 import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.NoSuchRoleException;
@@ -211,6 +212,29 @@ public class TestRole extends TestBase {
     Assertions.assertThrows(RuntimeException.class, () -> 
gravitinoClient.deleteRole(roleName));
   }
 
+  @Test
+  public void testListRoleNames() throws Exception {
+    String rolePath = withSlash(String.format(API_METALAKES_ROLES_PATH, 
metalakeName, ""));
+
+    NameListResponse listResponse = new NameListResponse(new String[] 
{"role1", "role2"});
+    buildMockResource(Method.GET, rolePath, null, listResponse, SC_OK);
+
+    Assertions.assertArrayEquals(new String[] {"role1", "role2"}, 
gravitinoClient.listRoleNames());
+
+    ErrorResponse errRespNoMetalake =
+        ErrorResponse.notFound(NoSuchMetalakeException.class.getSimpleName(), 
"metalake not found");
+    buildMockResource(Method.GET, rolePath, null, errRespNoMetalake, 
SC_NOT_FOUND);
+    Exception ex =
+        Assertions.assertThrows(
+            NoSuchMetalakeException.class, () -> 
gravitinoClient.listRoleNames());
+    Assertions.assertEquals("metalake not found", ex.getMessage());
+
+    // Test RuntimeException
+    ErrorResponse errResp = ErrorResponse.internalError("internal error");
+    buildMockResource(Method.GET, rolePath, null, errResp, SC_SERVER_ERROR);
+    Assertions.assertThrows(RuntimeException.class, () -> 
gravitinoClient.listRoleNames());
+  }
+
   private RoleDTO mockRoleDTO(String name) {
     SecurableObject securableObject =
         SecurableObjects.ofCatalog("catalog", 
Lists.newArrayList(Privileges.UseCatalog.allow()));
diff --git 
a/common/src/main/java/org/apache/gravitino/dto/authorization/RoleDTO.java 
b/common/src/main/java/org/apache/gravitino/dto/authorization/RoleDTO.java
index 9ce0b0da0..d0c51753e 100644
--- a/common/src/main/java/org/apache/gravitino/dto/authorization/RoleDTO.java
+++ b/common/src/main/java/org/apache/gravitino/dto/authorization/RoleDTO.java
@@ -158,7 +158,7 @@ public class RoleDTO implements Role {
     /**
      * Sets the securable objects of the role.
      *
-     * @param securableObjects The securableObjects of the role.
+     * @param securableObjects The securable objects of the role.
      * @return The builder instance.
      */
     public S withSecurableObjects(SecurableObjectDTO[] securableObjects) {
@@ -186,9 +186,7 @@ public class RoleDTO implements Role {
     public RoleDTO build() {
       Preconditions.checkArgument(StringUtils.isNotBlank(name), "name cannot 
be null or empty");
       Preconditions.checkArgument(audit != null, "audit cannot be null");
-      Preconditions.checkArgument(
-          securableObjects != null && securableObjects.length != 0,
-          "securable objects can't null or empty");
+      Preconditions.checkArgument(securableObjects != null, "securable objects 
can't null");
 
       return new RoleDTO(name, properties, securableObjects, audit);
     }
diff --git 
a/common/src/main/java/org/apache/gravitino/dto/responses/RoleResponse.java 
b/common/src/main/java/org/apache/gravitino/dto/responses/RoleResponse.java
index ca9c3960e..d200a24c8 100644
--- a/common/src/main/java/org/apache/gravitino/dto/responses/RoleResponse.java
+++ b/common/src/main/java/org/apache/gravitino/dto/responses/RoleResponse.java
@@ -65,7 +65,6 @@ public class RoleResponse extends BaseResponse {
         StringUtils.isNotBlank(role.name()), "role 'name' must not be null and 
empty");
     Preconditions.checkArgument(role.auditInfo() != null, "role 'auditInfo' 
must not be null");
     Preconditions.checkArgument(
-        role.securableObjects() != null && !role.securableObjects().isEmpty(),
-        "role 'securableObjects' can't null or empty");
+        role.securableObjects() != null, "role 'securable objects' can't 
null");
   }
 }
diff --git a/core/src/main/java/org/apache/gravitino/EntityStore.java 
b/core/src/main/java/org/apache/gravitino/EntityStore.java
index dcb27f022..d26dbf13b 100644
--- a/core/src/main/java/org/apache/gravitino/EntityStore.java
+++ b/core/src/main/java/org/apache/gravitino/EntityStore.java
@@ -20,9 +20,7 @@ package org.apache.gravitino;
 
 import java.io.Closeable;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 import java.util.function.Function;
 import org.apache.gravitino.Entity.EntityType;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
@@ -66,7 +64,7 @@ public interface EntityStore extends Closeable {
    */
   default <E extends Entity & HasIdentifier> List<E> list(
       Namespace namespace, Class<E> type, EntityType entityType) throws 
IOException {
-    return list(namespace, type, entityType, Collections.emptySet());
+    return list(namespace, type, entityType, true /* allFields */);
   }
 
   /**
@@ -80,14 +78,15 @@ public interface EntityStore extends Closeable {
    * @param namespace the namespace of the entities
    * @param type the detailed type of the entity
    * @param entityType the general type of the entity
-   * @param skippingFields Some fields may have a relatively high acquisition 
cost, EntityStore
-   *     provides an optional setting to avoid fetching these high-cost fields 
to improve the
-   *     performance.
+   * @param allFields Some fields may have a relatively high acquisition cost, 
EntityStore provides
+   *     an optional setting to avoid fetching these high-cost fields to 
improve the performance. If
+   *     true, the method will fetch all the fields, Otherwise, the method 
will fetch all the fields
+   *     except for high-cost fields.
    * @return the list of entities
    * @throws IOException if the list operation fails
    */
   default <E extends Entity & HasIdentifier> List<E> list(
-      Namespace namespace, Class<E> type, EntityType entityType, Set<Field> 
skippingFields)
+      Namespace namespace, Class<E> type, EntityType entityType, boolean 
allFields)
       throws IOException {
     throw new UnsupportedOperationException("Don't support to skip fields");
   }
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/AccessControlDispatcher.java
 
b/core/src/main/java/org/apache/gravitino/authorization/AccessControlDispatcher.java
index fbeebd944..95cb304de 100644
--- 
a/core/src/main/java/org/apache/gravitino/authorization/AccessControlDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/authorization/AccessControlDispatcher.java
@@ -236,5 +236,14 @@ public interface AccessControlDispatcher {
    * @throws NoSuchMetalakeException If the Metalake with the given name does 
not exist.
    * @throws RuntimeException If deleting the Role encounters storage issues.
    */
-  public boolean deleteRole(String metalake, String role) throws 
NoSuchMetalakeException;
+  boolean deleteRole(String metalake, String role) throws 
NoSuchMetalakeException;
+
+  /**
+   * Lists the role names.
+   *
+   * @param metalake The Metalake of the Role.
+   * @return The role name list.
+   * @throws NoSuchMetalakeException If the Metalake with the given name does 
not exist.
+   */
+  String[] listRoleNames(String metalake) throws NoSuchMetalakeException;
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java
 
b/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java
index 222b1ffb5..8872afade 100644
--- 
a/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java
+++ 
b/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java
@@ -143,6 +143,11 @@ public class AccessControlManager implements 
AccessControlDispatcher {
     return roleManager.deleteRole(metalake, role);
   }
 
+  @Override
+  public String[] listRoleNames(String metalake) throws 
NoSuchMetalakeException {
+    return roleManager.listRoleNames(metalake);
+  }
+
   @VisibleForTesting
   RoleManager getRoleManager() {
     return roleManager;
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java 
b/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
index 457a7f5ff..8b195894f 100644
--- a/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
+++ b/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
@@ -19,6 +19,7 @@
 
 package org.apache.gravitino.authorization;
 
+import com.google.common.collect.Sets;
 import java.io.IOException;
 import java.time.Instant;
 import java.util.List;
@@ -27,14 +28,15 @@ import org.apache.gravitino.Entity;
 import org.apache.gravitino.EntityAlreadyExistsException;
 import org.apache.gravitino.EntityStore;
 import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
+import org.apache.gravitino.exceptions.NoSuchMetalakeException;
 import org.apache.gravitino.exceptions.NoSuchRoleException;
 import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
 import org.apache.gravitino.meta.AuditInfo;
 import org.apache.gravitino.meta.RoleEntity;
 import org.apache.gravitino.storage.IdGenerator;
 import org.apache.gravitino.utils.PrincipalUtils;
-import org.glassfish.jersey.internal.guava.Sets;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -46,6 +48,7 @@ import org.slf4j.LoggerFactory;
 class RoleManager {
 
   private static final Logger LOG = LoggerFactory.getLogger(RoleManager.class);
+  private static final String METALAKE_DOES_NOT_EXIST_MSG = "Metalake %s does 
not exist";
   private final EntityStore store;
   private final IdGenerator idGenerator;
 
@@ -129,6 +132,22 @@ class RoleManager {
     }
   }
 
+  String[] listRoleNames(String metalake) {
+    try {
+      AuthorizationUtils.checkMetalakeExists(metalake);
+      Namespace namespace = AuthorizationUtils.ofRoleNamespace(metalake);
+      return store.list(namespace, RoleEntity.class, 
Entity.EntityType.ROLE).stream()
+          .map(Role::name)
+          .toArray(String[]::new);
+    } catch (NoSuchEntityException e) {
+      LOG.warn("Metalake {} does not exist", metalake, e);
+      throw new NoSuchMetalakeException(METALAKE_DOES_NOT_EXIST_MSG, metalake);
+    } catch (IOException ioe) {
+      LOG.error("Listing user under metalake {} failed due to storage issues", 
metalake, ioe);
+      throw new RuntimeException(ioe);
+    }
+  }
+
   private RoleEntity getRoleEntity(NameIdentifier identifier) {
     try {
       return store.get(identifier, Entity.EntityType.ROLE, RoleEntity.class);
diff --git 
a/core/src/main/java/org/apache/gravitino/authorization/UserGroupManager.java 
b/core/src/main/java/org/apache/gravitino/authorization/UserGroupManager.java
index 4b7b4f2d8..be1b687f3 100644
--- 
a/core/src/main/java/org/apache/gravitino/authorization/UserGroupManager.java
+++ 
b/core/src/main/java/org/apache/gravitino/authorization/UserGroupManager.java
@@ -23,11 +23,9 @@ import java.io.IOException;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Set;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.EntityAlreadyExistsException;
 import org.apache.gravitino.EntityStore;
-import org.apache.gravitino.Field;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
@@ -40,7 +38,6 @@ import org.apache.gravitino.meta.GroupEntity;
 import org.apache.gravitino.meta.UserEntity;
 import org.apache.gravitino.storage.IdGenerator;
 import org.apache.gravitino.utils.PrincipalUtils;
-import org.glassfish.jersey.internal.guava.Sets;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -117,26 +114,23 @@ class UserGroupManager {
   }
 
   String[] listUserNames(String metalake) {
-    Set<Field> skippingFields = Sets.newHashSet();
-    skippingFields.add(UserEntity.ROLE_NAMES);
-    skippingFields.add(UserEntity.ROLE_IDS);
 
-    return Arrays.stream(listUsersInternal(metalake, skippingFields))
+    return Arrays.stream(listUsersInternal(metalake, false /* allFields */))
         .map(User::name)
         .toArray(String[]::new);
   }
 
   User[] listUsers(String metalake) {
-    return listUsersInternal(metalake, Collections.emptySet());
+    return listUsersInternal(metalake, true /* allFields */);
   }
 
-  private User[] listUsersInternal(String metalake, Set<Field> skippingFields) 
{
+  private User[] listUsersInternal(String metalake, boolean allFields) {
     try {
       AuthorizationUtils.checkMetalakeExists(metalake);
 
       Namespace namespace = AuthorizationUtils.ofUserNamespace(metalake);
       return store
-          .list(namespace, UserEntity.class, Entity.EntityType.USER, 
skippingFields)
+          .list(namespace, UserEntity.class, Entity.EntityType.USER, allFields)
           .toArray(new User[0]);
     } catch (NoSuchEntityException e) {
       LOG.error("Metalake {} does not exist", metalake, e);
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 730563862..7882e9c8a 100644
--- 
a/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
@@ -157,4 +157,9 @@ public class AccessControlHookDispatcher implements 
AccessControlDispatcher {
   public boolean deleteRole(String metalake, String role) throws 
NoSuchMetalakeException {
     return dispatcher.deleteRole(metalake, role);
   }
+
+  @Override
+  public String[] listRoleNames(String metalake) throws 
NoSuchMetalakeException {
+    return dispatcher.listRoleNames(metalake);
+  }
 }
diff --git a/core/src/main/java/org/apache/gravitino/meta/RoleEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/RoleEntity.java
index d1b3bbfe9..a96f6e0f5 100644
--- a/core/src/main/java/org/apache/gravitino/meta/RoleEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/RoleEntity.java
@@ -46,8 +46,8 @@ public class RoleEntity implements Role, Entity, Auditable, 
HasIdentifier {
   public static final Field AUDIT_INFO =
       Field.required("audit_info", AuditInfo.class, "The audit details of the 
role entity.");
 
-  public static final Field SECURABLE_OBJECT =
-      Field.required("securable_objects", List.class, "The securable objects 
of the role entity.");
+  public static final Field SECURABLE_OBJECTS =
+      Field.optional("securable_objects", List.class, "The securable objects 
of the role entity.");
 
   private Long id;
   private String name;
@@ -91,15 +91,6 @@ public class RoleEntity implements Role, Entity, Auditable, 
HasIdentifier {
    */
   @Override
   public List<SecurableObject> securableObjects() {
-    // The securable object is a special kind of entities. Some entity types 
aren't the securable
-    // object, such as
-    // User, Role, etc.
-    // The securable object identifier must be unique.
-    // Gravitino assumes that the identifiers of the entities may be the same 
if they have different
-    // types.
-    // So one type of them can't be the securable object at least if there are 
the two same
-    // identifier
-    // entities .
     return securableObjects;
   }
 
@@ -115,7 +106,7 @@ public class RoleEntity implements Role, Entity, Auditable, 
HasIdentifier {
     fields.put(NAME, name);
     fields.put(AUDIT_INFO, auditInfo);
     fields.put(PROPERTIES, properties);
-    fields.put(SECURABLE_OBJECT, securableObjects);
+    fields.put(SECURABLE_OBJECTS, securableObjects);
 
     return Collections.unmodifiableMap(fields);
   }
diff --git a/core/src/main/java/org/apache/gravitino/meta/UserEntity.java 
b/core/src/main/java/org/apache/gravitino/meta/UserEntity.java
index df47215b4..c71d731a9 100644
--- a/core/src/main/java/org/apache/gravitino/meta/UserEntity.java
+++ b/core/src/main/java/org/apache/gravitino/meta/UserEntity.java
@@ -23,7 +23,6 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
 import lombok.ToString;
 import org.apache.gravitino.Auditable;
 import org.apache.gravitino.Entity;
@@ -31,7 +30,6 @@ import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.authorization.User;
-import org.glassfish.jersey.internal.guava.Sets;
 
 /** A class representing a user metadata entity in Apache Gravitino. */
 @ToString
@@ -156,22 +154,6 @@ public class UserEntity implements User, Entity, 
Auditable, HasIdentifier {
     return roleIds;
   }
 
-  /**
-   * Get the set of all the fields.
-   *
-   * @return The set of all the fields.
-   */
-  public static Set<Field> fieldSet() {
-    Set<Field> fields = Sets.newHashSet();
-    fields.add(ID);
-    fields.add(NAME);
-    fields.add(AUDIT_INFO);
-    fields.add(ROLE_IDS);
-    fields.add(ROLE_NAMES);
-
-    return Collections.unmodifiableSet(fields);
-  }
-
   @Override
   public boolean equals(Object o) {
     if (this == o) return true;
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java 
b/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
index 549c5fec2..2b9a6d0e4 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
@@ -26,13 +26,11 @@ import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.Function;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.EntityAlreadyExistsException;
-import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
@@ -90,8 +88,7 @@ public class JDBCBackend implements RelationalBackend {
 
   @Override
   public <E extends Entity & HasIdentifier> List<E> list(
-      Namespace namespace, Entity.EntityType entityType, Set<Field> 
skippingFields)
-      throws IOException {
+      Namespace namespace, Entity.EntityType entityType, boolean allFields) 
throws IOException {
     switch (entityType) {
       case METALAKE:
         return (List<E>) MetalakeMetaService.getInstance().listMetalakes();
@@ -108,8 +105,9 @@ public class JDBCBackend implements RelationalBackend {
       case TAG:
         return (List<E>) 
TagMetaService.getInstance().listTagsByNamespace(namespace);
       case USER:
-        return (List<E>)
-            UserMetaService.getInstance().listUsersByNamespace(namespace, 
skippingFields);
+        return (List<E>) 
UserMetaService.getInstance().listUsersByNamespace(namespace, allFields);
+      case ROLE:
+        return (List<E>) 
RoleMetaService.getInstance().listRolesByNamespace(namespace);
       default:
         throw new UnsupportedEntityTypeException(
             "Unsupported entity type: %s for list operation", entityType);
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java
index fe85754b4..cae0c7b4d 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalBackend.java
@@ -21,12 +21,10 @@ package org.apache.gravitino.storage.relational;
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.List;
-import java.util.Set;
 import java.util.function.Function;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.EntityAlreadyExistsException;
-import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
@@ -50,9 +48,10 @@ public interface RelationalBackend
    *
    * @param namespace The parent namespace of these entities.
    * @param entityType The type of these entities.
-   * @param skippingFields Some fields may have a relatively high acquisition 
cost, EntityStore
-   *     provide an optional setting to avoid fetching these high-cost fields 
to improve the
-   *     performance.
+   * @param allFields Some fields may have a relatively high acquisition cost, 
EntityStore provide
+   *     an optional setting to avoid fetching these high-cost fields to 
improve the performance. If
+   *     true, the method will fetch all the fields, Otherwise, the method 
will fetch all the fields
+   *     except for high-cost fields.
    * @return The list of entities associated with the given parent namespace 
and entityType, or null
    *     if the entities does not exist.
    * @throws NoSuchEntityException If the corresponding parent entity of these 
list entities cannot
@@ -60,7 +59,7 @@ public interface RelationalBackend
    * @throws IOException If the store operation fails
    */
   <E extends Entity & HasIdentifier> List<E> list(
-      Namespace namespace, Entity.EntityType entityType, Set<Field> 
skippingFields)
+      Namespace namespace, Entity.EntityType entityType, boolean allFields)
       throws NoSuchEntityException, IOException;
 
   /**
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
index d7403729f..c95db1a07 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
@@ -22,9 +22,7 @@ import static 
org.apache.gravitino.Configs.ENTITY_RELATIONAL_STORE;
 
 import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.List;
-import java.util.Set;
 import java.util.function.Function;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
@@ -32,7 +30,6 @@ import org.apache.gravitino.Entity;
 import org.apache.gravitino.EntityAlreadyExistsException;
 import org.apache.gravitino.EntitySerDe;
 import org.apache.gravitino.EntityStore;
-import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
@@ -92,14 +89,14 @@ public class RelationalEntityStore
   @Override
   public <E extends Entity & HasIdentifier> List<E> list(
       Namespace namespace, Class<E> type, Entity.EntityType entityType) throws 
IOException {
-    return backend.list(namespace, entityType, Collections.emptySet());
+    return backend.list(namespace, entityType, false);
   }
 
   @Override
   public <E extends Entity & HasIdentifier> List<E> list(
-      Namespace namespace, Class<E> type, Entity.EntityType entityType, 
Set<Field> skippingFields)
+      Namespace namespace, Class<E> type, Entity.EntityType entityType, 
boolean allFields)
       throws IOException {
-    return backend.list(namespace, entityType, skippingFields);
+    return backend.list(namespace, entityType, allFields);
   }
 
   @Override
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/RoleMetaMapper.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/RoleMetaMapper.java
index d7478c3b7..6b155f498 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/RoleMetaMapper.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/RoleMetaMapper.java
@@ -65,6 +65,9 @@ public interface RoleMetaMapper {
       @Param("metadataObjectId") Long metadataObjectId,
       @Param("metadataObjectType") String metadataObjectType);
 
+  @SelectProvider(type = RoleMetaSQLProviderFactory.class, method = 
"listRolePOsByMetalake")
+  List<RolePO> listRolePOsByMetalake(@Param("metalakeName") String 
metalakeName);
+
   @InsertProvider(type = RoleMetaSQLProviderFactory.class, method = 
"insertRoleMeta")
   void insertRoleMeta(@Param("roleMeta") RolePO rolePO);
 
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/RoleMetaSQLProviderFactory.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/RoleMetaSQLProviderFactory.java
index 5d160677e..2aedd02eb 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/RoleMetaSQLProviderFactory.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/RoleMetaSQLProviderFactory.java
@@ -71,6 +71,10 @@ public class RoleMetaSQLProviderFactory {
     return getProvider().listRolesByMetadataObjectIdAndType(metadataObjectId, 
type);
   }
 
+  public static String listRolePOsByMetalake(@Param("metalakeName") String 
metalakeName) {
+    return getProvider().listRolePOsByMetalake(metalakeName);
+  }
+
   public static String insertRoleMeta(@Param("roleMeta") RolePO rolePO) {
     return getProvider().insertRoleMeta(rolePO);
   }
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/RoleMetaBaseSQLProvider.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/RoleMetaBaseSQLProvider.java
index 95749ab65..b2dc12137 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/RoleMetaBaseSQLProvider.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/mapper/provider/base/RoleMetaBaseSQLProvider.java
@@ -22,6 +22,7 @@ import static 
org.apache.gravitino.storage.relational.mapper.RoleMetaMapper.GROU
 import static 
org.apache.gravitino.storage.relational.mapper.RoleMetaMapper.ROLE_TABLE_NAME;
 import static 
org.apache.gravitino.storage.relational.mapper.RoleMetaMapper.USER_ROLE_RELATION_TABLE_NAME;
 
+import org.apache.gravitino.storage.relational.mapper.MetalakeMetaMapper;
 import org.apache.gravitino.storage.relational.mapper.SecurableObjectMapper;
 import org.apache.gravitino.storage.relational.po.RolePO;
 import org.apache.ibatis.annotations.Param;
@@ -93,6 +94,20 @@ public class RoleMetaBaseSQLProvider {
         + " AND ro.deleted_at = 0 AND se.deleted_at = 0";
   }
 
+  public String listRolePOsByMetalake(@Param("metalakeName") String 
metalakeName) {
+    return "SELECT rt.role_id as roleId, rt.role_name as roleName,"
+        + " rt.metalake_id as metalakeId, rt.properties as properties,"
+        + " rt.audit_info as auditInfo, rt.current_version as currentVersion,"
+        + " rt.last_version as lastVersion, rt.deleted_at as deletedAt"
+        + " FROM "
+        + ROLE_TABLE_NAME
+        + " rt JOIN "
+        + MetalakeMetaMapper.TABLE_NAME
+        + " mt ON rt.metalake_id = mt.metalake_id"
+        + " WHERE mt.metalake_name = #{metalakeName}"
+        + " AND rt.deleted_at = 0 AND mt.deleted_at = 0";
+  }
+
   public String insertRoleMeta(@Param("roleMeta") RolePO rolePO) {
     return "INSERT INTO "
         + ROLE_TABLE_NAME
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
index 4560b74e0..1e914f59a 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
@@ -20,11 +20,13 @@ package org.apache.gravitino.storage.relational.service;
 
 import com.google.common.collect.Lists;
 import java.io.IOException;
+import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.gravitino.Entity;
 import org.apache.gravitino.MetadataObject;
 import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
 import org.apache.gravitino.authorization.AuthorizationUtils;
 import org.apache.gravitino.authorization.SecurableObject;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
@@ -237,6 +239,22 @@ public class RoleMetaService {
         SecurableObjectMapper.class, mapper -> 
mapper.listSecurableObjectsByRoleId(roleId));
   }
 
+  public List<RoleEntity> listRolesByNamespace(Namespace namespace) {
+    AuthorizationUtils.checkRoleNamespace(namespace);
+    String metalakeName = namespace.level(0);
+
+    List<RolePO> rolePOs =
+        SessionUtils.getWithoutCommit(
+            RoleMetaMapper.class, mapper -> 
mapper.listRolePOsByMetalake(metalakeName));
+
+    return rolePOs.stream()
+        .map(
+            po ->
+                POConverters.fromRolePO(
+                    po, Collections.emptyList(), 
AuthorizationUtils.ofRoleNamespace(metalakeName)))
+        .collect(Collectors.toList());
+  }
+
   public int deleteRoleMetasByLegacyTimeline(long legacyTimeline, int limit) {
     int[] roleDeletedCount = new int[] {0};
     int[] userRoleRelDeletedCount = new int[] {0};
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/SupportsDesiredFields.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/SupportsDesiredFields.java
deleted file mode 100644
index 42978fa50..000000000
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/SupportsDesiredFields.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.storage.relational.service;
-
-import java.util.Set;
-import org.apache.gravitino.Field;
-
-/** The handler supports to skip fields to acquire part desired fields. */
-interface SupportsDesiredFields<R> {
-
-  /**
-   * The fields which could be desired.
-   *
-   * @return The fields which are desired.
-   */
-  Set<Field> desiredFields();
-
-  /**
-   * The return value of the handler.
-   *
-   * @return The return value of the handler.
-   */
-  R execute();
-}
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/SupportsDesiredFieldsHandlers.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/SupportsDesiredFieldsHandlers.java
deleted file mode 100644
index b5b10a7b6..000000000
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/SupportsDesiredFieldsHandlers.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.storage.relational.service;
-
-import com.google.common.collect.Lists;
-import java.util.List;
-import java.util.Set;
-import org.apache.gravitino.Field;
-
-/**
- * This class is the collection wrapper of SupportsDesiredFields handler. The 
class will contain all
- * the handlers can proceed the data. We can choose different handlers 
according to the desired
- * fields to acquire better performance.
- *
- * @param <T> The value type which the handler will return.
- */
-class SupportsDesiredFieldsHandlers<T> {
-  private final List<SupportsDesiredFields<T>> methods = Lists.newArrayList();
-
-  // We should put the low-cost handler into the front of the list.
-  void addHandler(SupportsDesiredFields<T> supportsSkippingFields) {
-    methods.add(supportsSkippingFields);
-  }
-
-  T execute(Set<Field> desiredFields) {
-    for (SupportsDesiredFields<T> method : methods) {
-      if (method.desiredFields().containsAll(desiredFields)) {
-        return method.execute();
-      }
-    }
-
-    throw new IllegalArgumentException("Don't support skip fields");
-  }
-}
diff --git 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/UserMetaService.java
 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/UserMetaService.java
index f64b4ab40..a8991da14 100644
--- 
a/core/src/main/java/org/apache/gravitino/storage/relational/service/UserMetaService.java
+++ 
b/core/src/main/java/org/apache/gravitino/storage/relational/service/UserMetaService.java
@@ -30,7 +30,6 @@ import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.gravitino.Entity;
-import org.apache.gravitino.Field;
 import org.apache.gravitino.HasIdentifier;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
@@ -249,18 +248,34 @@ public class UserMetaService {
     return newEntity;
   }
 
-  public List<UserEntity> listUsersByNamespace(Namespace namespace, Set<Field> 
skippingFields) {
+  public List<UserEntity> listUsersByNamespace(Namespace namespace, boolean 
allFields) {
     AuthorizationUtils.checkUserNamespace(namespace);
     String metalakeName = namespace.level(0);
 
-    SupportsDesiredFieldsHandlers<List<UserEntity>> handlers =
-        new SupportsDesiredFieldsHandlers<>();
-    handlers.addHandler(new ListDesiredRolesHandler(metalakeName));
-    handlers.addHandler(new ListAllFieldsHandler(metalakeName));
-
-    Set<Field> desiredFields = Sets.newHashSet(UserEntity.fieldSet());
-    desiredFields.removeAll(skippingFields);
-    return handlers.execute(desiredFields);
+    if (allFields) {
+      Long metalakeId = 
MetalakeMetaService.getInstance().getMetalakeIdByName(metalakeName);
+      List<ExtendedUserPO> userPOs =
+          SessionUtils.getWithoutCommit(
+              UserMetaMapper.class, mapper -> 
mapper.listExtendedUserPOsByMetalakeId(metalakeId));
+      return userPOs.stream()
+          .map(
+              po ->
+                  POConverters.fromExtendedUserPO(
+                      po, AuthorizationUtils.ofUserNamespace(metalakeName)))
+          .collect(Collectors.toList());
+    } else {
+      List<UserPO> userPOs =
+          SessionUtils.getWithoutCommit(
+              UserMetaMapper.class, mapper -> 
mapper.listUserPOsByMetalake(metalakeName));
+      return userPOs.stream()
+          .map(
+              po ->
+                  POConverters.fromUserPO(
+                      po,
+                      Collections.emptyList(),
+                      AuthorizationUtils.ofUserNamespace(metalakeName)))
+          .collect(Collectors.toList());
+    }
   }
 
   public int deleteUserMetasByLegacyTimeline(long legacyTimeline, int limit) {
@@ -282,63 +297,4 @@ public class UserMetaService {
 
     return userDeletedCount[0] + userRoleRelDeletedCount[0];
   }
-
-  private static class ListDesiredRolesHandler implements 
SupportsDesiredFields<List<UserEntity>> {
-    private final String metalakeName;
-
-    ListDesiredRolesHandler(String metalakeName) {
-      this.metalakeName = metalakeName;
-    }
-
-    @Override
-    public Set<Field> desiredFields() {
-      Set<Field> requiredFields = Sets.newHashSet(UserEntity.fieldSet());
-      requiredFields.remove(UserEntity.ROLE_IDS);
-      requiredFields.remove(UserEntity.ROLE_NAMES);
-
-      return requiredFields;
-    }
-
-    @Override
-    public List<UserEntity> execute() {
-      List<UserPO> userPOs =
-          SessionUtils.getWithoutCommit(
-              UserMetaMapper.class, mapper -> 
mapper.listUserPOsByMetalake(metalakeName));
-      return userPOs.stream()
-          .map(
-              po ->
-                  POConverters.fromUserPO(
-                      po,
-                      Collections.emptyList(),
-                      AuthorizationUtils.ofUserNamespace(metalakeName)))
-          .collect(Collectors.toList());
-    }
-  }
-
-  private static class ListAllFieldsHandler implements 
SupportsDesiredFields<List<UserEntity>> {
-    final String metalakeName;
-
-    ListAllFieldsHandler(String metalakeName) {
-      this.metalakeName = metalakeName;
-    }
-
-    @Override
-    public Set<Field> desiredFields() {
-      return UserEntity.fieldSet();
-    }
-
-    @Override
-    public List<UserEntity> execute() {
-      Long metalakeId = 
MetalakeMetaService.getInstance().getMetalakeIdByName(metalakeName);
-      List<ExtendedUserPO> userPOs =
-          SessionUtils.getWithoutCommit(
-              UserMetaMapper.class, mapper -> 
mapper.listExtendedUserPOsByMetalakeId(metalakeId));
-      return userPOs.stream()
-          .map(
-              po ->
-                  POConverters.fromExtendedUserPO(
-                      po, AuthorizationUtils.ofUserNamespace(metalakeName)))
-          .collect(Collectors.toList());
-    }
-  }
 }
diff --git 
a/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
 
b/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
index 1c7a26dec..6dfaf54fe 100644
--- 
a/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
+++ 
b/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
@@ -397,6 +397,32 @@ public class TestAccessControlManager {
     Assertions.assertFalse(dropped1);
   }
 
+  @Test
+  public void testListRoles() {
+    Map<String, String> props = ImmutableMap.of("k1", "v1");
+
+    accessControlManager.createRole(
+        "metalake_list",
+        "testList1",
+        props,
+        Lists.newArrayList(
+            SecurableObjects.ofCatalog(
+                "catalog", 
Lists.newArrayList(Privileges.UseCatalog.allow()))));
+
+    accessControlManager.createRole(
+        "metalake_list",
+        "testList2",
+        props,
+        Lists.newArrayList(
+            SecurableObjects.ofCatalog(
+                "catalog", 
Lists.newArrayList(Privileges.UseCatalog.allow()))));
+
+    // Test to list roles
+    String[] actualRoles = accessControlManager.listRoleNames("metalake_list");
+    Arrays.sort(actualRoles);
+    Assertions.assertArrayEquals(new String[] {"testList1", "testList2"}, 
actualRoles);
+  }
+
   private void testProperties(Map<String, String> expectedProps, Map<String, 
String> testProps) {
     expectedProps.forEach(
         (k, v) -> {
diff --git a/core/src/test/java/org/apache/gravitino/meta/TestEntity.java 
b/core/src/test/java/org/apache/gravitino/meta/TestEntity.java
index f4c32dca7..f657f01a9 100644
--- a/core/src/test/java/org/apache/gravitino/meta/TestEntity.java
+++ b/core/src/test/java/org/apache/gravitino/meta/TestEntity.java
@@ -296,7 +296,7 @@ public class TestEntity {
         Lists.newArrayList(
             SecurableObjects.ofCatalog(
                 catalogName, 
Lists.newArrayList(Privileges.UseCatalog.allow()))),
-        fields.get(RoleEntity.SECURABLE_OBJECT));
+        fields.get(RoleEntity.SECURABLE_OBJECTS));
 
     RoleEntity roleWithoutFields =
         RoleEntity.builder()
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/TestJDBCBackend.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/TestJDBCBackend.java
index b141eb696..c67c9697a 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/TestJDBCBackend.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/TestJDBCBackend.java
@@ -45,7 +45,6 @@ import java.sql.SQLException;
 import java.sql.Statement;
 import java.time.Instant;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -715,29 +714,26 @@ public class TestJDBCBackend {
 
     // meta data list
     List<BaseMetalake> metaLakes =
-        backend.list(metalake.namespace(), Entity.EntityType.METALAKE, 
Collections.emptySet());
+        backend.list(metalake.namespace(), Entity.EntityType.METALAKE, true);
     assertTrue(metaLakes.contains(metalake));
 
     List<CatalogEntity> catalogs =
-        backend.list(catalog.namespace(), Entity.EntityType.CATALOG, 
Collections.emptySet());
+        backend.list(catalog.namespace(), Entity.EntityType.CATALOG, true);
     assertTrue(catalogs.contains(catalog));
 
-    List<SchemaEntity> schemas =
-        backend.list(schema.namespace(), Entity.EntityType.SCHEMA, 
Collections.emptySet());
+    List<SchemaEntity> schemas = backend.list(schema.namespace(), 
Entity.EntityType.SCHEMA, true);
     assertTrue(schemas.contains(schema));
 
-    List<TableEntity> tables =
-        backend.list(table.namespace(), Entity.EntityType.TABLE, 
Collections.emptySet());
+    List<TableEntity> tables = backend.list(table.namespace(), 
Entity.EntityType.TABLE, true);
     assertTrue(tables.contains(table));
 
     List<FilesetEntity> filesets =
-        backend.list(fileset.namespace(), Entity.EntityType.FILESET, 
Collections.emptySet());
+        backend.list(fileset.namespace(), Entity.EntityType.FILESET, true);
     assertFalse(filesets.contains(fileset));
     assertTrue(filesets.contains(filesetV2));
     assertEquals("2", 
filesets.get(filesets.indexOf(filesetV2)).properties().get("version"));
 
-    List<TopicEntity> topics =
-        backend.list(topic.namespace(), Entity.EntityType.TOPIC, 
Collections.emptySet());
+    List<TopicEntity> topics = backend.list(topic.namespace(), 
Entity.EntityType.TOPIC, true);
     assertTrue(topics.contains(topic));
 
     RoleEntity roleEntity = backend.get(role.nameIdentifier(), 
Entity.EntityType.ROLE);
@@ -763,8 +759,7 @@ public class TestJDBCBackend {
 
     TagEntity tagEntity = backend.get(tag.nameIdentifier(), 
Entity.EntityType.TAG);
     assertEquals(tag, tagEntity);
-    List<TagEntity> tags =
-        backend.list(tag.namespace(), Entity.EntityType.TAG, 
Collections.emptySet());
+    List<TagEntity> tags = backend.list(tag.namespace(), 
Entity.EntityType.TAG, true);
     assertTrue(tags.contains(tag));
     assertEquals(1, tags.size());
 
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestRoleMetaService.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestRoleMetaService.java
index d811b8b59..4a781f018 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestRoleMetaService.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestRoleMetaService.java
@@ -106,6 +106,55 @@ class TestRoleMetaService extends TestJDBCBackend {
     Assertions.assertEquals(role1, 
roleMetaService.getRoleByIdentifier(role1.nameIdentifier()));
   }
 
+  @Test
+  void testListRoles() throws IOException {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+    BaseMetalake metalake =
+        createBaseMakeLake(RandomIdGenerator.INSTANCE.nextId(), metalakeName, 
auditInfo);
+    backend.insert(metalake, false);
+
+    CatalogEntity catalog =
+        createCatalog(
+            RandomIdGenerator.INSTANCE.nextId(), Namespace.of("metalake"), 
"catalog", auditInfo);
+    backend.insert(catalog, false);
+
+    RoleEntity role1 =
+        createRoleEntity(
+            RandomIdGenerator.INSTANCE.nextId(),
+            AuthorizationUtils.ofRoleNamespace(metalakeName),
+            "role1",
+            auditInfo,
+            SecurableObjects.ofCatalog(
+                "catalog", Lists.newArrayList(Privileges.UseCatalog.allow())),
+            ImmutableMap.of("k1", "v1"));
+
+    RoleEntity role2 =
+        createRoleEntity(
+            RandomIdGenerator.INSTANCE.nextId(),
+            AuthorizationUtils.ofRoleNamespace(metalakeName),
+            "role2",
+            auditInfo,
+            SecurableObjects.ofCatalog(
+                "catalog", Lists.newArrayList(Privileges.UseCatalog.allow())),
+            ImmutableMap.of("k1", "v1"));
+
+    backend.insert(role1, false);
+    backend.insert(role2, false);
+
+    RoleMetaService roleMetaService = RoleMetaService.getInstance();
+    List<RoleEntity> actualRoles =
+        
roleMetaService.listRolesByNamespace(AuthorizationUtils.ofRoleNamespace(metalakeName));
+    actualRoles.sort(Comparator.comparing(RoleEntity::name));
+    List<RoleEntity> expectRoles = Lists.newArrayList(role1, role2);
+    Assertions.assertEquals(expectRoles.size(), actualRoles.size());
+    for (int index = 0; index < expectRoles.size(); index++) {
+      RoleEntity expectRole = expectRoles.get(index);
+      RoleEntity actualRole = actualRoles.get(index);
+      Assertions.assertEquals(expectRole.name(), actualRole.name());
+    }
+  }
+
   @Test
   void insertRole() throws IOException {
     AuditInfo auditInfo =
diff --git 
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestUserMetaService.java
 
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestUserMetaService.java
index 0d037317d..0efd886ee 100644
--- 
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestUserMetaService.java
+++ 
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestUserMetaService.java
@@ -27,7 +27,6 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.time.Instant;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Optional;
@@ -173,7 +172,7 @@ class TestUserMetaService extends TestJDBCBackend {
     UserMetaService userMetaService = UserMetaService.getInstance();
     List<UserEntity> actualUsers =
         userMetaService.listUsersByNamespace(
-            AuthorizationUtils.ofUserNamespace(metalakeName), 
Collections.emptySet());
+            AuthorizationUtils.ofUserNamespace(metalakeName), true);
     actualUsers.sort(Comparator.comparing(UserEntity::name));
     List<UserEntity> expectUsers = Lists.newArrayList(user1, user2);
     Assertions.assertEquals(expectUsers.size(), actualUsers.size());
diff --git 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/authorization/AccessControlIT.java
 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/authorization/AccessControlIT.java
index 2345dbcdd..a4167f1af 100644
--- 
a/integration-test/src/test/java/org/apache/gravitino/integration/test/authorization/AccessControlIT.java
+++ 
b/integration-test/src/test/java/org/apache/gravitino/integration/test/authorization/AccessControlIT.java
@@ -176,6 +176,16 @@ public class AccessControlIT extends AbstractIT {
 
     Assertions.assertEquals(roleName, role.name());
     Assertions.assertEquals(properties, role.properties());
+    assertSecurableObjects(Lists.newArrayList(metalakeObject), 
role.securableObjects());
+
+    // List roles
+    String anotherRoleName = "another-role";
+    metalake.createRole(anotherRoleName, properties, 
Lists.newArrayList(metalakeObject));
+    String[] roleNames = metalake.listRoleNames();
+    Arrays.sort(roleNames);
+
+    Assertions.assertEquals(
+        Lists.newArrayList(anotherRoleName, roleName), 
Arrays.asList(roleNames));
 
     // Verify the object
     Assertions.assertEquals(1, role.securableObjects().size());
@@ -303,4 +313,22 @@ public class AccessControlIT extends AbstractIT {
     metalake.removeGroup(groupName);
     metalake.deleteRole(roleName);
   }
+
+  private static void assertSecurableObjects(
+      List<SecurableObject> expect, List<SecurableObject> actual) {
+    Assertions.assertEquals(expect.size(), actual.size());
+    for (int index = 0; index < expect.size(); index++) {
+      Assertions.assertEquals(expect.get(index).fullName(), 
actual.get(index).fullName());
+      Assertions.assertEquals(expect.get(index).type(), 
actual.get(index).type());
+      List<Privilege> expectPrivileges = expect.get(index).privileges();
+      List<Privilege> actualPrivileges = actual.get(index).privileges();
+      Assertions.assertEquals(expectPrivileges.size(), 
actualPrivileges.size());
+      for (int priIndex = 0; priIndex < expectPrivileges.size(); priIndex++) {
+        Assertions.assertEquals(
+            expectPrivileges.get(priIndex).name(), 
actualPrivileges.get(priIndex).name());
+        Assertions.assertEquals(
+            actualPrivileges.get(priIndex).condition(), 
actualPrivileges.get(priIndex).condition());
+      }
+    }
+  }
 }
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 1fa8a443f..b006471e3 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
@@ -44,6 +44,7 @@ import org.apache.gravitino.authorization.SecurableObjects;
 import org.apache.gravitino.dto.authorization.SecurableObjectDTO;
 import org.apache.gravitino.dto.requests.RoleCreateRequest;
 import org.apache.gravitino.dto.responses.DeleteResponse;
+import org.apache.gravitino.dto.responses.NameListResponse;
 import org.apache.gravitino.dto.responses.RoleResponse;
 import org.apache.gravitino.dto.util.DTOConverters;
 import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
@@ -68,6 +69,27 @@ public class RoleOperations {
     this.accessControlManager = 
GravitinoEnv.getInstance().accessControlDispatcher();
   }
 
+  @GET
+  @Produces("application/vnd.gravitino.v1+json")
+  @Timed(name = "list-role." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
+  @ResponseMetered(name = "list-role", absolute = true)
+  public Response listRoles(@PathParam("metalake") String metalake) {
+    try {
+      return Utils.doAs(
+          httpRequest,
+          () ->
+              TreeLockUtils.doWithTreeLock(
+                  NameIdentifier.of(metalake),
+                  LockType.READ,
+                  () -> {
+                    String[] names = 
accessControlManager.listRoleNames(metalake);
+                    return Utils.ok(new NameListResponse(names));
+                  }));
+    } catch (Exception e) {
+      return ExceptionHandlers.handleRoleException(OperationType.LIST, "", 
metalake, e);
+    }
+  }
+
   @GET
   @Path("{role}")
   @Produces("application/vnd.gravitino.v1+json")
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 a00f91b4d..eb365d1ac 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
@@ -54,6 +54,7 @@ import org.apache.gravitino.dto.requests.RoleCreateRequest;
 import org.apache.gravitino.dto.responses.DeleteResponse;
 import org.apache.gravitino.dto.responses.ErrorConstants;
 import org.apache.gravitino.dto.responses.ErrorResponse;
+import org.apache.gravitino.dto.responses.NameListResponse;
 import org.apache.gravitino.dto.responses.RoleResponse;
 import org.apache.gravitino.dto.util.DTOConverters;
 import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
@@ -454,4 +455,51 @@ public class TestRoleOperations extends JerseyTest {
         NoSuchMetadataObjectException.class,
         () -> RoleOperations.checkSecurableObject("metalake", 
DTOConverters.toDTO(fileset)));
   }
+
+  @Test
+  public void testListRoleNames() {
+    when(manager.listRoleNames(any())).thenReturn(new String[] {"role"});
+
+    Response resp =
+        target("/metalakes/metalake1/roles/")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .get();
+    Assertions.assertEquals(Response.Status.OK.getStatusCode(), 
resp.getStatus());
+
+    NameListResponse listResponse = resp.readEntity(NameListResponse.class);
+    Assertions.assertEquals(0, listResponse.getCode());
+
+    Assertions.assertEquals(1, listResponse.getNames().length);
+    Assertions.assertEquals("role", listResponse.getNames()[0]);
+
+    // Test to throw NoSuchMetalakeException
+    doThrow(new NoSuchMetalakeException("mock 
error")).when(manager).listRoleNames(any());
+    Response resp1 =
+        target("/metalakes/metalake1/roles/")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .get();
+
+    Assertions.assertEquals(Response.Status.NOT_FOUND.getStatusCode(), 
resp1.getStatus());
+
+    ErrorResponse errorResponse = resp1.readEntity(ErrorResponse.class);
+    Assertions.assertEquals(ErrorConstants.NOT_FOUND_CODE, 
errorResponse.getCode());
+    Assertions.assertEquals(NoSuchMetalakeException.class.getSimpleName(), 
errorResponse.getType());
+
+    // Test to throw internal RuntimeException
+    doThrow(new RuntimeException("mock 
error")).when(manager).listRoleNames(any());
+    Response resp3 =
+        target("/metalakes/metalake1/roles")
+            .request(MediaType.APPLICATION_JSON_TYPE)
+            .accept("application/vnd.gravitino.v1+json")
+            .get();
+
+    Assertions.assertEquals(
+        Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), 
resp3.getStatus());
+
+    ErrorResponse errorResponse2 = resp3.readEntity(ErrorResponse.class);
+    Assertions.assertEquals(ErrorConstants.INTERNAL_ERROR_CODE, 
errorResponse2.getCode());
+    Assertions.assertEquals(RuntimeException.class.getSimpleName(), 
errorResponse2.getType());
+  }
 }

Reply via email to