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

jackietien pushed a commit to branch rc/2.0.6
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit bb4f5c8c9a086014f8ddad78b189fe18d06fd276
Author: wenyanshi-123 <[email protected]>
AuthorDate: Sat Sep 20 22:07:58 2025 +0800

    Append user index field for User (#16432)
    (cherry picked from commit 4ebfe9eab86d8dc56d7534bf3b24346e3692bd08)
---
 .../org/apache/iotdb/db/it/IoTDBRestServiceIT.java |   6 +-
 .../org/apache/iotdb/db/it/auth/IoTDBAuthIT.java   | 109 ++++++++++++---------
 .../iotdb/db/it/auth/IoTDBRelationalAuthIT.java    |  22 +++--
 .../manual/IoTDBPipeMetaHistoricalIT.java          |   4 +-
 .../treemodel/manual/IoTDBPipePermissionIT.java    |   4 +-
 .../response/auth/PermissionInfoResp.java          |  11 +++
 .../confignode/manager/PermissionManager.java      |   4 +
 .../pipe/event/PipeConfigRegionSnapshotEvent.java  |  35 ++++---
 .../receiver/protocol/IoTDBConfigNodeReceiver.java |   3 +-
 .../payload/PipeTransferConfigSnapshotSealReq.java |   8 +-
 .../sink/protocol/IoTDBConfigRegionAirGapSink.java |   3 +-
 .../pipe/sink/protocol/IoTDBConfigRegionSink.java  |   3 +-
 .../pipe/source/ConfigRegionListeningQueue.java    |  25 ++++-
 .../pipe/source/IoTDBConfigRegionSource.java       |   3 +-
 .../iotdb/confignode/persistence/AuthorInfo.java   |  34 ++++++-
 .../schema/CNPhysicalPlanGenerator.java            |  14 ++-
 .../schema/ConfigNodeSnapshotParser.java           |   5 +-
 .../thrift/ConfigNodeRPCServiceProcessor.java      |   2 +
 .../pipe/sink/PipeConfigNodeThriftRequestTest.java |   3 +-
 .../persistence/CNPhysicalPlanGeneratorTest.java   |  15 +--
 .../org/apache/iotdb/db/auth/AuthorityChecker.java |  22 ++++-
 .../org/apache/iotdb/db/auth/entity/UserTest.java  |   4 +-
 .../db/auth/user/LocalFileUserAccessorTest.java    |  19 ++--
 .../commons/auth/authorizer/BasicAuthorizer.java   |  11 +++
 .../iotdb/commons/auth/authorizer/IAuthorizer.java |  17 ++++
 .../iotdb/commons/auth/entity/IEntityAccessor.java |  15 +++
 .../org/apache/iotdb/commons/auth/entity/Role.java |  18 ++++
 .../org/apache/iotdb/commons/auth/entity/User.java |  36 ++++++-
 .../iotdb/commons/auth/role/BasicRoleManager.java  |  22 +++++
 .../iotdb/commons/auth/role/IEntityManager.java    |   9 ++
 .../commons/auth/role/LocalFileRoleAccessor.java   |  64 +++++++++++-
 .../iotdb/commons/auth/user/BasicUserManager.java  |  60 +++++++++++-
 .../commons/auth/user/LocalFileUserAccessor.java   |  47 +++++++--
 .../commons/auth/user/LocalFileUserManager.java    |   1 +
 .../schema/column/ColumnHeaderConstant.java        |   7 ++
 .../org/apache/iotdb/commons/utils/IOUtils.java    |  30 ++++++
 .../src/main/thrift/confignode.thrift              |   8 ++
 37 files changed, 585 insertions(+), 118 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBRestServiceIT.java 
b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBRestServiceIT.java
index 99d4b1560f0..7d6d8774385 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBRestServiceIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBRestServiceIT.java
@@ -1399,6 +1399,7 @@ public class IoTDBRestServiceIT {
     List<Object> columnNames =
         new ArrayList<Object>() {
           {
+            add(ColumnHeaderConstant.USER_ID);
             add(ColumnHeaderConstant.USER);
           }
         };
@@ -1409,7 +1410,7 @@ public class IoTDBRestServiceIT {
           }
         };
     Assert.assertEquals(columnNames, columnNamesResult);
-    Assert.assertEquals(values1, valuesResult.get(0));
+    Assert.assertEquals(values1, valuesResult.get(1));
   }
 
   public void selectCount(CloseableHttpClient httpClient) {
@@ -2062,6 +2063,7 @@ public class IoTDBRestServiceIT {
     List<Object> columnNames =
         new ArrayList<Object>() {
           {
+            add(ColumnHeaderConstant.USER_ID);
             add(ColumnHeaderConstant.USER);
           }
         };
@@ -2072,7 +2074,7 @@ public class IoTDBRestServiceIT {
           }
         };
     Assert.assertEquals(columnNames, columnNamesResult);
-    Assert.assertEquals(values1, valuesResult.get(0));
+    Assert.assertEquals(values1, valuesResult.get(1));
   }
 
   public void selectCountV2(CloseableHttpClient httpClient) {
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
index 231d9ce7215..97bccfd263a 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
@@ -94,6 +94,17 @@ public class IoTDBAuthIT {
         Assert.assertThrows(
             SQLException.class,
             () -> userStmt.execute("GRANT WRITE_SCHEMA ON root.a TO USER 
tempuser"));
+        Assert.assertThrows(
+            SQLException.class, () -> userStmt.execute("LIST PRIVILEGES OF 
USER root"));
+
+        ResultSet resultSet = userStmt.executeQuery("LIST USER");
+        Assert.assertTrue(resultSet.next());
+        Assert.assertEquals("10000", resultSet.getString(1));
+        Assert.assertEquals("tempuser", resultSet.getString(2));
+        Assert.assertFalse(resultSet.next());
+
+        resultSet = userStmt.executeQuery("LIST PRIVILEGES OF USER tempuser");
+        Assert.assertFalse(resultSet.next());
 
         //  2. admin grant all privileges to user tempuser, So tempuser can do 
anything.
         adminStmt.execute("GRANT ALL ON root.** TO USER tempuser");
@@ -479,7 +490,7 @@ public class IoTDBAuthIT {
 
     try {
       ResultSet resultSet = adminStmt.executeQuery("LIST USER");
-      String ans = "root,\n";
+      String ans = "0,root,\n";
       try {
         validateResultSet(resultSet, ans);
 
@@ -488,17 +499,17 @@ public class IoTDBAuthIT {
         }
         resultSet = adminStmt.executeQuery("LIST USER");
         ans =
-            "root,\n"
-                + "user0,\n"
-                + "user1,\n"
-                + "user2,\n"
-                + "user3,\n"
-                + "user4,\n"
-                + "user5,\n"
-                + "user6,\n"
-                + "user7,\n"
-                + "user8,\n"
-                + "user9,\n";
+            "0,root,\n"
+                + "10000,user0,\n"
+                + "10001,user1,\n"
+                + "10002,user2,\n"
+                + "10003,user3,\n"
+                + "10004,user4,\n"
+                + "10005,user5,\n"
+                + "10006,user6,\n"
+                + "10007,user7,\n"
+                + "10008,user8,\n"
+                + "10009,user9,\n";
         validateResultSet(resultSet, ans);
 
         for (int i = 0; i < 10; i++) {
@@ -507,7 +518,13 @@ public class IoTDBAuthIT {
           }
         }
         resultSet = adminStmt.executeQuery("LIST USER");
-        ans = "root,\n" + "user1,\n" + "user3,\n" + "user5,\n" + "user7,\n" + 
"user9,\n";
+        ans =
+            "0,root,\n"
+                + "10001,user1,\n"
+                + "10003,user3,\n"
+                + "10005,user5,\n"
+                + "10007,user7,\n"
+                + "10009,user9,\n";
         validateResultSet(resultSet, ans);
       } finally {
         resultSet.close();
@@ -591,7 +608,7 @@ public class IoTDBAuthIT {
         ans = "role1,\nrole2,\n";
         validateResultSet(resultSet, ans);
         resultSet = userStmt.executeQuery("LIST USER OF ROLE role1");
-        ans = "user1,\nuser2,\n";
+        ans = "10000,user1,\n10001,user2,\n";
         validateResultSet(resultSet, ans);
       } finally {
         userStmt.close();
@@ -782,25 +799,25 @@ public class IoTDBAuthIT {
 
       ResultSet resultSet = adminStmt.executeQuery("LIST USER OF ROLE dalao");
       String ans =
-          "DailySecurity,\n"
-              + "DoubleLight,\n"
-              + "East,\n"
-              + "Eastwards,\n"
-              + "GoldLuck,\n"
-              + "GoodWoods,\n"
-              + "HealthHonor,\n"
-              + "HighFly,\n"
-              + "Moon,\n"
-              + "Persistence,\n"
-              + "RayBud,\n"
-              + "ScentEffusion,\n"
-              + "Smart,\n"
-              + "SunComparison,\n";
+          "10011,DailySecurity,\n"
+              + "10006,DoubleLight,\n"
+              + "10010,East,\n"
+              + "10007,Eastwards,\n"
+              + "10005,GoldLuck,\n"
+              + "10003,GoodWoods,\n"
+              + "10004,HealthHonor,\n"
+              + "10000,HighFly,\n"
+              + "10012,Moon,\n"
+              + "10002,Persistence,\n"
+              + "10013,RayBud,\n"
+              + "10008,ScentEffusion,\n"
+              + "10009,Smart,\n"
+              + "10001,SunComparison,\n";
       try {
         validateResultSet(resultSet, ans);
 
         resultSet = adminStmt.executeQuery("LIST USER OF ROLE zhazha");
-        ans = "RiverSky,\n";
+        ans = "10014,RiverSky,\n";
         validateResultSet(resultSet, ans);
 
         adminStmt.execute("REVOKE ROLE zhazha from RiverSky");
@@ -855,23 +872,25 @@ public class IoTDBAuthIT {
     try (Connection userCon = EnvFactory.getEnv().getConnection("tempuser", 
"temppw123456");
         Statement userStmt = userCon.createStatement()) {
       try {
-        Assert.assertThrows(SQLException.class, () -> userStmt.execute("LIST 
USER"));
-        // with list user privilege
-        adminStmt.execute("GRANT MANAGE_USER on root.** TO USER tempuser");
+        String ans = "10010,tempuser,\n";
         ResultSet resultSet = userStmt.executeQuery("LIST USER");
-        String ans =
-            "root,\n"
-                + "tempuser,\n"
-                + "user0,\n"
-                + "user1,\n"
-                + "user2,\n"
-                + "user3,\n"
-                + "user4,\n"
-                + "user5,\n"
-                + "user6,\n"
-                + "user7,\n"
-                + "user8,\n"
-                + "user9,\n";
+        validateResultSet(resultSet, ans);
+        // with list user privilege
+        adminStmt.execute("GRANT SECURITY on root.** TO USER tempuser");
+        resultSet = userStmt.executeQuery("LIST USER");
+        ans =
+            "0,root,\n"
+                + "10010,tempuser,\n"
+                + "10000,user0,\n"
+                + "10001,user1,\n"
+                + "10002,user2,\n"
+                + "10003,user3,\n"
+                + "10004,user4,\n"
+                + "10005,user5,\n"
+                + "10006,user6,\n"
+                + "10007,user7,\n"
+                + "10008,user8,\n"
+                + "10009,user9,\n";
         validateResultSet(resultSet, ans);
       } finally {
         userStmt.close();
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java
index f2f0aef18ba..a364630906f 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java
@@ -66,6 +66,16 @@ public class IoTDBRelationalAuthIT {
         Statement adminStmt = adminCon.createStatement()) {
 
       adminStmt.execute("create user testuser 'password123456'");
+      try (Connection userCon =
+              EnvFactory.getEnv()
+                  .getConnection("testuser", "password123456", 
BaseEnv.TABLE_SQL_DIALECT);
+          Statement userStmt = userCon.createStatement()) {
+        ResultSet resultSet = userStmt.executeQuery("LIST USER");
+        Assert.assertTrue(resultSet.next());
+        Assert.assertEquals("10000", resultSet.getString(1));
+        Assert.assertEquals("testuser", resultSet.getString(2));
+        Assert.assertFalse(resultSet.next());
+      }
       adminStmt.execute("create database testdb");
       adminStmt.execute("GRANT MANAGE_USER to user testuser");
       Assert.assertThrows(
@@ -119,7 +129,7 @@ public class IoTDBRelationalAuthIT {
       adminStmt.execute("create role testrole");
       adminStmt.execute("GRANT ROLE testrole to testuser");
       rs = adminStmt.executeQuery("LIST USER OF ROLE testrole");
-      TestUtils.assertResultSetEqual(rs, "User,", 
Collections.singleton("testuser,"));
+      TestUtils.assertResultSetEqual(rs, "UserId,User,", 
Collections.singleton("10000,testuser,"));
       rs = adminStmt.executeQuery("LIST ROLE OF USER testuser");
       TestUtils.assertResultSetEqual(rs, "Role,", 
Collections.singleton("testrole,"));
     }
@@ -600,11 +610,11 @@ public class IoTDBRelationalAuthIT {
 
       ResultSet resultSet = adminStmt.executeQuery("List user");
       Set<String> resultSetList = new HashSet<>();
-      resultSetList.add("root,");
-      resultSetList.add("testuser,");
-      resultSetList.add("!@#$%^*()_+-=1,");
-      resultSetList.add("!@#$%^*()_+-=2,");
-      TestUtils.assertResultSetEqual(resultSet, "User,", resultSetList);
+      resultSetList.add("0,root,");
+      resultSetList.add("10000,testuser,");
+      resultSetList.add("10001,!@#$%^*()_+-=1,");
+      resultSetList.add("10002,!@#$%^*()_+-=2,");
+      TestUtils.assertResultSetEqual(resultSet, "UserId,User,", resultSetList);
       resultSet = adminStmt.executeQuery("List role");
       TestUtils.assertResultSetEqual(resultSet, "Role,", 
Collections.singleton("!@#$%^*()_+-=3,"));
       adminStmt.execute("GRANT role \"!@#$%^*()_+-=3\" to  
\"!@#$%^*()_+-=1\"");
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeMetaHistoricalIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeMetaHistoricalIT.java
index 4e4120e1481..acbf514e8ad 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeMetaHistoricalIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeMetaHistoricalIT.java
@@ -236,8 +236,8 @@ public class IoTDBPipeMetaHistoricalIT extends 
AbstractPipeDualTreeModelManualIT
       TestUtils.assertDataEventuallyOnEnv(
           receiverEnv,
           "list user of role `admin`",
-          ColumnHeaderConstant.USER + ",",
-          Collections.singleton("thulab,"));
+          ColumnHeaderConstant.USER_ID + "," + ColumnHeaderConstant.USER + ",",
+          Collections.singleton("10000,thulab,"));
       TestUtils.assertDataEventuallyOnEnv(
           receiverEnv,
           "list privileges of role `admin`",
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java
index bdd9b850c8a..fc87b74b378 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java
@@ -152,8 +152,8 @@ public class IoTDBPipePermissionIT extends 
AbstractPipeDualTreeModelManualIT {
       TestUtils.assertDataEventuallyOnEnv(
           receiverEnv,
           "list user",
-          "User,",
-          new HashSet<>(Arrays.asList("root,", "user,", "thulab,")));
+          "UserId,User,",
+          new HashSet<>(Arrays.asList("0,root,", "10001,user,", 
"10000,thulab,")));
       final Set<String> expectedResSet = new HashSet<>();
       expectedResSet.add(
           
"root.ln.wf02.wt01.temperature,null,root.ln,INT64,PLAIN,LZ4,null,null,null,null,BASE,");
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/auth/PermissionInfoResp.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/auth/PermissionInfoResp.java
index 70413d552db..de440336753 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/auth/PermissionInfoResp.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/auth/PermissionInfoResp.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.confignode.consensus.response.auth;
 
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp;
 import org.apache.iotdb.consensus.common.DataSet;
 
@@ -32,6 +33,8 @@ public class PermissionInfoResp implements DataSet {
   private String tag;
   private List<String> memberList;
 
+  private List<TListUserInfo> usersInfo;
+
   private TPermissionInfoResp permissionInfoResp;
 
   public PermissionInfoResp() {}
@@ -62,6 +65,14 @@ public class PermissionInfoResp implements DataSet {
     return memberList;
   }
 
+  public void setUsersInfo(List<TListUserInfo> usersInfo) {
+    this.usersInfo = usersInfo;
+  }
+
+  public List<TListUserInfo> getUsersInfo() {
+    return usersInfo;
+  }
+
   public TPermissionInfoResp getPermissionInfoResp() {
     return permissionInfoResp;
   }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java
index 46549c67449..605e5785e9e 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java
@@ -141,4 +141,8 @@ public class PermissionManager {
   public TPermissionInfoResp getUser(String username) throws AuthException {
     return authorInfo.getUser(username);
   }
+
+  public String getUserName(long userId) throws AuthException {
+    return authorInfo.getUserName(userId);
+  }
 }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/event/PipeConfigRegionSnapshotEvent.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/event/PipeConfigRegionSnapshotEvent.java
index df76f4fe7df..5a8465e99d5 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/event/PipeConfigRegionSnapshotEvent.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/event/PipeConfigRegionSnapshotEvent.java
@@ -60,6 +60,8 @@ public class PipeConfigRegionSnapshotEvent extends 
PipeSnapshotEvent
       SNAPSHOT_FILE_TYPE_2_CONFIG_PHYSICAL_PLAN_TYPE_MAP = new 
EnumMap<>(CNSnapshotFileType.class);
   private CNSnapshotFileType fileType;
 
+  private String authUserName = "";
+
   static {
     SNAPSHOT_FILE_TYPE_2_CONFIG_PHYSICAL_PLAN_TYPE_MAP.put(
         CNSnapshotFileType.ROLE,
@@ -136,6 +138,14 @@ public class PipeConfigRegionSnapshotEvent extends 
PipeSnapshotEvent
     this.fileType = type;
   }
 
+  public String getAuthUserName() {
+    return authUserName;
+  }
+
+  public void setAuthUserName(String authUserName) {
+    this.authUserName = authUserName;
+  }
+
   public File getSnapshotFile() {
     return new File(snapshotPath);
   }
@@ -195,17 +205,20 @@ public class PipeConfigRegionSnapshotEvent extends 
PipeSnapshotEvent
       final boolean skipIfNoPrivileges,
       final long startTime,
       final long endTime) {
-    return new PipeConfigRegionSnapshotEvent(
-        snapshotPath,
-        templateFilePath,
-        fileType,
-        pipeName,
-        creationTime,
-        pipeTaskMeta,
-        treePattern,
-        tablePattern,
-        userName,
-        skipIfNoPrivileges);
+    PipeConfigRegionSnapshotEvent pipeConfigRegionSnapshotEvent =
+        new PipeConfigRegionSnapshotEvent(
+            snapshotPath,
+            templateFilePath,
+            fileType,
+            pipeName,
+            creationTime,
+            pipeTaskMeta,
+            treePattern,
+            tablePattern,
+            userName,
+            skipIfNoPrivileges);
+    pipeConfigRegionSnapshotEvent.setAuthUserName(authUserName);
+    return pipeConfigRegionSnapshotEvent;
   }
 
   @Override
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
index 5d00996ec61..d52ad5cd7e8 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
@@ -1033,7 +1033,8 @@ public class IoTDBConfigNodeReceiver extends 
IoTDBFileReceiver {
             Paths.get(fileAbsolutePaths.get(0)),
             fileAbsolutePaths.size() > 1 ? Paths.get(fileAbsolutePaths.get(1)) 
: null,
             CNSnapshotFileType.deserialize(
-                
Byte.parseByte(parameters.get(PipeTransferConfigSnapshotSealReq.FILE_TYPE))));
+                
Byte.parseByte(parameters.get(PipeTransferConfigSnapshotSealReq.FILE_TYPE))),
+            parameters.getOrDefault("authUserName", ""));
     if (Objects.isNull(generator)) {
       throw new IOException(
           String.format("The config region snapshots %s cannot be parsed.", 
fileAbsolutePaths));
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/payload/PipeTransferConfigSnapshotSealReq.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/payload/PipeTransferConfigSnapshotSealReq.java
index 1162e8ade94..90a13f32e49 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/payload/PipeTransferConfigSnapshotSealReq.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/payload/PipeTransferConfigSnapshotSealReq.java
@@ -58,7 +58,8 @@ public class PipeTransferConfigSnapshotSealReq extends 
PipeTransferFileSealReqV2
       final String templateFileName,
       final long templateFileLength,
       final CNSnapshotFileType fileType,
-      final String typeString)
+      final String typeString,
+      final String authUserName)
       throws IOException {
     final Map<String, String> parameters = new HashMap<>();
     parameters.put(ColumnHeaderConstant.PATH_PATTERN, treePattern);
@@ -72,6 +73,7 @@ public class PipeTransferConfigSnapshotSealReq extends 
PipeTransferFileSealReqV2
     }
     parameters.put(FILE_TYPE, Byte.toString(fileType.getType()));
     parameters.put(ColumnHeaderConstant.TYPE, typeString);
+    parameters.put("authUserName", authUserName);
 
     return (PipeTransferConfigSnapshotSealReq)
         new PipeTransferConfigSnapshotSealReq()
@@ -103,7 +105,8 @@ public class PipeTransferConfigSnapshotSealReq extends 
PipeTransferFileSealReqV2
       final String templateFileName,
       final long templateFileLength,
       final CNSnapshotFileType fileType,
-      final String typeString)
+      final String typeString,
+      final String authUserName)
       throws IOException {
     final Map<String, String> parameters = new HashMap<>();
     parameters.put(ColumnHeaderConstant.PATH_PATTERN, treePattern);
@@ -117,6 +120,7 @@ public class PipeTransferConfigSnapshotSealReq extends 
PipeTransferFileSealReqV2
     }
     parameters.put(FILE_TYPE, Byte.toString(fileType.getType()));
     parameters.put(ColumnHeaderConstant.TYPE, typeString);
+    parameters.put("authUserName", authUserName);
     return new PipeTransferConfigSnapshotSealReq()
         .convertToTPipeTransferSnapshotSealBytes(
             Objects.nonNull(templateFileName)
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionAirGapSink.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionAirGapSink.java
index c9c8ded4cf0..0ca96e021e3 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionAirGapSink.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionAirGapSink.java
@@ -232,7 +232,8 @@ public class IoTDBConfigRegionAirGapSink extends 
IoTDBAirGapSink {
             Objects.nonNull(templateFile) ? templateFile.getName() : null,
             Objects.nonNull(templateFile) ? templateFile.length() : 0,
             pipeConfigRegionSnapshotEvent.getFileType(),
-            pipeConfigRegionSnapshotEvent.toSealTypeString()))) {
+            pipeConfigRegionSnapshotEvent.toSealTypeString(),
+            pipeConfigRegionSnapshotEvent.getAuthUserName()))) {
       final String errorMessage =
           String.format("Seal config region snapshot %s error. Socket %s.", 
snapshot, socket);
       // Send handshake because we don't know whether the receiver side 
configNode
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionSink.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionSink.java
index a1e9239ccb2..7738ddbca12 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionSink.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionSink.java
@@ -241,7 +241,8 @@ public class IoTDBConfigRegionSink extends IoTDBSslSyncSink 
{
                   Objects.nonNull(templateFile) ? templateFile.getName() : 
null,
                   Objects.nonNull(templateFile) ? templateFile.length() : 0,
                   snapshotEvent.getFileType(),
-                  snapshotEvent.toSealTypeString()));
+                  snapshotEvent.toSealTypeString(),
+                  snapshotEvent.getAuthUserName()));
       rateLimitIfNeeded(
           snapshotEvent.getPipeName(),
           snapshotEvent.getCreationTime(),
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/ConfigRegionListeningQueue.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/ConfigRegionListeningQueue.java
index 94ea7a54dde..b70a039da44 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/ConfigRegionListeningQueue.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/ConfigRegionListeningQueue.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.confignode.manager.pipe.source;
 
+import org.apache.iotdb.commons.auth.AuthException;
 import org.apache.iotdb.commons.auth.user.LocalFileUserAccessor;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.exception.MetadataException;
@@ -135,25 +136,39 @@ public class ConfigRegionListeningQueue extends 
AbstractPipeListeningQueue
               && snapshotPath
                   .toFile()
                   .getName()
-                  .equals(AuthorityChecker.SUPER_USER + 
IoTDBConstant.PROFILE_SUFFIX)
+                  .equals(AuthorityChecker.SUPER_USER_ID_IN_STR + 
IoTDBConstant.PROFILE_SUFFIX)
           || type == CNSnapshotFileType.USER_ROLE
               && snapshotPath
                   .toFile()
                   .getName()
                   .equals(
-                      AuthorityChecker.SUPER_USER
+                      AuthorityChecker.SUPER_USER_ID_IN_STR
                           + LocalFileUserAccessor.ROLE_SUFFIX
-                          + IoTDBConstant.PROFILE_SUFFIX)) {
+                          + IoTDBConstant.PROFILE_SUFFIX)
+          || snapshotPath.toFile().getName().equals("user_id.profile")) {
         continue;
       }
       final Path templateFilePath = snapshotPathInfo.getLeft().getRight();
-      events.add(
+      PipeConfigRegionSnapshotEvent curEvent =
           new PipeConfigRegionSnapshotEvent(
               snapshotPath.toString(),
               Objects.nonNull(templateFilePath) && 
templateFilePath.toFile().length() > 0
                   ? templateFilePath.toString()
                   : null,
-              snapshotPathInfo.getRight()));
+              snapshotPathInfo.getRight());
+      if (type == CNSnapshotFileType.USER_ROLE) {
+        long userId = 
Long.parseLong(snapshotPath.toFile().getName().split("_")[0]);
+        try {
+          curEvent.setAuthUserName(
+              ConfigNode.getInstance()
+                  .getConfigManager()
+                  .getPermissionManager()
+                  .getUserName(userId));
+        } catch (AuthException e) {
+          LOGGER.warn("Failed to collect user name for user id {}", userId, e);
+        }
+      }
+      events.add(curEvent);
     }
     tryListen(events);
   }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/IoTDBConfigRegionSource.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/IoTDBConfigRegionSource.java
index ef1e08f8536..fc965624c1d 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/IoTDBConfigRegionSource.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/IoTDBConfigRegionSource.java
@@ -203,7 +203,8 @@ public class IoTDBConfigRegionSource extends 
IoTDBNonDataRegionSource {
             Objects.nonNull(snapshotEvent.getTemplateFile())
                 ? Paths.get(snapshotEvent.getTemplateFile().getPath())
                 : null,
-            snapshotEvent.getFileType());
+            snapshotEvent.getFileType(),
+            snapshotEvent.getAuthUserName());
   }
 
   @Override
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java
index bafb17ce87e..d6a0ee2a283 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java
@@ -47,6 +47,7 @@ import 
org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan;
 import org.apache.iotdb.confignode.consensus.response.auth.PermissionInfoResp;
 import org.apache.iotdb.confignode.manager.ConfigManager;
 import org.apache.iotdb.confignode.rpc.thrift.TAuthizedPatternTreeResp;
+import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp;
 import org.apache.iotdb.confignode.rpc.thrift.TRoleResp;
 import org.apache.iotdb.confignode.rpc.thrift.TUserResp;
@@ -63,6 +64,7 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -502,7 +504,19 @@ public class AuthorInfo implements SnapshotProcessor {
 
   public PermissionInfoResp executeListUsers(final AuthorPlan plan) throws 
AuthException {
     final PermissionInfoResp result = new PermissionInfoResp();
-    final List<String> userList = authorizer.listAllUsers();
+    final List<String> userList;
+    final List<TListUserInfo> userInfoList;
+    boolean hasPermissionToListOtherUsers = plan.getUserName().isEmpty();
+    if (!hasPermissionToListOtherUsers) {
+      // userList may be modified later
+      userList = new ArrayList<>(1);
+      userList.add(plan.getUserName());
+      User user = authorizer.getUser(plan.getUserName());
+      userInfoList = Collections.singletonList(user.convertToListUserInfo());
+    } else {
+      userList = authorizer.listAllUsers();
+      userInfoList = authorizer.listAllUsersInfo();
+    }
     if (!plan.getRoleName().isEmpty()) {
       final Role role = authorizer.getRole(plan.getRoleName());
       if (role == null) {
@@ -512,8 +526,19 @@ public class AuthorInfo implements SnapshotProcessor {
         return result;
       }
       final Iterator<String> itr = userList.iterator();
+      Set<String> toRemove = new HashSet<>();
       while (itr.hasNext()) {
-        User userObj = authorizer.getUser(itr.next());
+        String userName = itr.next();
+        User userObj = authorizer.getUser(userName);
+        if (userObj == null || !userObj.hasRole(plan.getRoleName())) {
+          itr.remove();
+          toRemove.add(userName);
+        }
+      }
+      userInfoList.removeIf(info -> toRemove.contains(info.username));
+      final Iterator<TListUserInfo> userInfoitr = userInfoList.iterator();
+      while (itr.hasNext()) {
+        User userObj = authorizer.getUser(userInfoitr.next().getUsername());
         if (userObj == null || !userObj.hasRole(plan.getRoleName())) {
           itr.remove();
         }
@@ -521,6 +546,7 @@ public class AuthorInfo implements SnapshotProcessor {
     }
     result.setTag(ColumnHeaderConstant.USER);
     result.setMemberInfo(userList);
+    result.setUsersInfo(userInfoList);
     result.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS));
     return result;
   }
@@ -665,6 +691,10 @@ public class AuthorInfo implements SnapshotProcessor {
     return result;
   }
 
+  public String getUserName(long userId) throws AuthException {
+    return authorizer.getUser(userId).getName();
+  }
+
   @Override
   public boolean processTakeSnapshot(File snapshotDir) throws TException, 
IOException {
     return authorizer.processTakeSnapshot(snapshotDir);
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java
index 33fb4b4ed6d..b81b14ef504 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java
@@ -103,14 +103,15 @@ public class CNPhysicalPlanGenerator
   private Exception latestException = null;
   private String userName;
 
-  public CNPhysicalPlanGenerator(final Path snapshotFilePath, final 
CNSnapshotFileType fileType)
+  public CNPhysicalPlanGenerator(
+      final Path snapshotFilePath, final CNSnapshotFileType fileType, final 
String userName)
       throws IOException {
     if (fileType == CNSnapshotFileType.SCHEMA) {
       logger.warn("schema_template need two files");
       return;
     }
     if (fileType == CNSnapshotFileType.USER_ROLE) {
-      userName = 
snapshotFilePath.getFileName().toString().split("_role.profile")[0];
+      this.userName = userName;
     }
     snapshotFileType = fileType;
     inputStream = Files.newInputStream(snapshotFilePath);
@@ -200,9 +201,14 @@ public class CNPhysicalPlanGenerator
       int tag = dataInputStream.readInt();
       boolean fromOldVersion = tag < 0;
       String user;
-      if (fromOldVersion) {
+      if (tag < 0) {
         user = readString(dataInputStream, STRING_ENCODING, strBufferLocal, -1 
* tag);
+      } else if (tag == 1) {
+        user = readString(dataInputStream, STRING_ENCODING, strBufferLocal);
       } else {
+        if (isUser) {
+          dataInputStream.readLong(); // skip userId since authorPlan do not 
demand it.
+        }
         user = readString(dataInputStream, STRING_ENCODING, strBufferLocal);
       }
 
@@ -226,7 +232,7 @@ public class CNPhysicalPlanGenerator
       final int privilegeMask = dataInputStream.readInt();
       generateGrantSysPlan(user, isUser, privilegeMask);
 
-      if (fromOldVersion) {
+      if (tag < 0) {
         while (dataInputStream.available() != 0) {
           final String path = readString(dataInputStream, STRING_ENCODING, 
strBufferLocal);
           final PartialPath priPath;
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigNodeSnapshotParser.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigNodeSnapshotParser.java
index 31b1cd56519..84126441694 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigNodeSnapshotParser.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigNodeSnapshotParser.java
@@ -166,7 +166,8 @@ public class ConfigNodeSnapshotParser {
   }
 
   public static CNPhysicalPlanGenerator translate2PhysicalPlan(
-      final Path path1, final Path path2, final CNSnapshotFileType type) 
throws IOException {
+      final Path path1, final Path path2, final CNSnapshotFileType type, final 
String userName)
+      throws IOException {
     if (path1 == null) {
       LOGGER.warn("Path1 should not be null");
       return null;
@@ -180,7 +181,7 @@ public class ConfigNodeSnapshotParser {
     if (type == CNSnapshotFileType.SCHEMA) {
       return new CNPhysicalPlanGenerator(path1, path2);
     } else {
-      return new CNPhysicalPlanGenerator(path1, type);
+      return new CNPhysicalPlanGenerator(path1, type, userName);
     }
   }
 }
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
index 431d0d4f952..77192cd5f34 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
@@ -687,6 +687,7 @@ public class ConfigNodeRPCServiceProcessor implements 
IConfigNodeRPCService.Ifac
     resp.setMemberInfo(dataSet.getMemberList());
     resp.setPermissionInfo(dataSet.getPermissionInfoResp());
     resp.setTag(dataSet.getTag());
+    resp.setUsersInfo(dataSet.getUsersInfo());
     return resp;
   }
 
@@ -729,6 +730,7 @@ public class ConfigNodeRPCServiceProcessor implements 
IConfigNodeRPCService.Ifac
     final TAuthorizerResp resp = new TAuthorizerResp(dataSet.getStatus());
     resp.setMemberInfo(dataSet.getMemberList());
     resp.setPermissionInfo(dataSet.getPermissionInfoResp());
+    resp.setUsersInfo(dataSet.getUsersInfo());
     resp.setTag(dataSet.getTag());
     return resp;
   }
diff --git 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/sink/PipeConfigNodeThriftRequestTest.java
 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/sink/PipeConfigNodeThriftRequestTest.java
index 0f729c7b4b0..5c578ebead8 100644
--- 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/sink/PipeConfigNodeThriftRequestTest.java
+++ 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/sink/PipeConfigNodeThriftRequestTest.java
@@ -96,7 +96,8 @@ public class PipeConfigNodeThriftRequestTest {
             templateInfoName,
             10,
             fileType,
-            typeString);
+            typeString,
+            "");
     PipeTransferConfigSnapshotSealReq deserializeReq =
         PipeTransferConfigSnapshotSealReq.fromTPipeTransferReq(req);
 
diff --git 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/CNPhysicalPlanGeneratorTest.java
 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/CNPhysicalPlanGeneratorTest.java
index 647b50fdcf5..19622c80906 100644
--- 
a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/CNPhysicalPlanGeneratorTest.java
+++ 
b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/CNPhysicalPlanGeneratorTest.java
@@ -178,7 +178,7 @@ public class CNPhysicalPlanGeneratorTest {
                 + ".profile");
 
     final CNPhysicalPlanGenerator planGenerator =
-        new CNPhysicalPlanGenerator(roleProfile.toPath(), 
CNSnapshotFileType.ROLE);
+        new CNPhysicalPlanGenerator(roleProfile.toPath(), 
CNSnapshotFileType.ROLE, "");
     int count = 0;
     for (ConfigPhysicalPlan authPlan : planGenerator) {
       Assert.assertTrue(answerSet.contains(authPlan.hashCode()));
@@ -260,11 +260,11 @@ public class CNPhysicalPlanGeneratorTest {
                 + File.separator
                 + USER_SNAPSHOT_FILE_NAME
                 + File.separator
-                + userName
+                + 10000
                 + ".profile");
 
     CNPhysicalPlanGenerator planGenerator =
-        new CNPhysicalPlanGenerator(userProfile.toPath(), 
CNSnapshotFileType.USER);
+        new CNPhysicalPlanGenerator(userProfile.toPath(), 
CNSnapshotFileType.USER, userName);
     int count = 0;
     // plan 1-4
     for (ConfigPhysicalPlan authPlan : planGenerator) {
@@ -278,10 +278,11 @@ public class CNPhysicalPlanGeneratorTest {
                 + File.separator
                 + USER_SNAPSHOT_FILE_NAME
                 + File.separator
-                + userName
+                + 10000
                 + "_role.profile");
     planGenerator =
-        new CNPhysicalPlanGenerator(roleListProfile.toPath(), 
CNSnapshotFileType.USER_ROLE);
+        new CNPhysicalPlanGenerator(
+            roleListProfile.toPath(), CNSnapshotFileType.USER_ROLE, userName);
     count = 0;
     // plan 5
     for (ConfigPhysicalPlan authPlan : planGenerator) {
@@ -345,7 +346,7 @@ public class CNPhysicalPlanGeneratorTest {
     }
     planGenerator.checkException();
     Assert.assertEquals(5, count);
-    planGenerator = new CNPhysicalPlanGenerator(ttlInfo.toPath(), 
CNSnapshotFileType.TTL);
+    planGenerator = new CNPhysicalPlanGenerator(ttlInfo.toPath(), 
CNSnapshotFileType.TTL, "");
     for (ConfigPhysicalPlan plan : planGenerator) {
       if (plan.getType() == ConfigPhysicalPlanType.SetTTL) {
         if (!new PartialPath(((SetTTLPlan) plan).getPathPattern())
@@ -510,7 +511,7 @@ public class CNPhysicalPlanGeneratorTest {
     }
     Assert.assertEquals(8, count);
 
-    planGenerator = new CNPhysicalPlanGenerator(ttlInfo.toPath(), 
CNSnapshotFileType.TTL);
+    planGenerator = new CNPhysicalPlanGenerator(ttlInfo.toPath(), 
CNSnapshotFileType.TTL, "");
     for (ConfigPhysicalPlan plan : planGenerator) {
       if (plan.getType() == ConfigPhysicalPlanType.SetTTL) {
         if (!new PartialPath(((SetTTLPlan) plan).getPathPattern())
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
index 71f064008df..988d69d0062 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
@@ -30,6 +30,7 @@ import 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
 import org.apache.iotdb.commons.service.metric.PerformanceOverviewMetrics;
 import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp;
 import org.apache.iotdb.confignode.rpc.thrift.TDBPrivilege;
+import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TPathPrivilege;
 import org.apache.iotdb.confignode.rpc.thrift.TRoleResp;
 import org.apache.iotdb.confignode.rpc.thrift.TTablePrivilege;
@@ -57,6 +58,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.LIST_USER_COLUMN_HEADERS;
 import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.LIST_USER_OR_ROLE_PRIVILEGES_COLUMN_HEADERS;
 
 // Authority checker is SingleTon working at datanode.
@@ -65,6 +67,8 @@ public class AuthorityChecker {
 
   public static final String SUPER_USER = 
CommonDescriptor.getInstance().getConfig().getAdminName();
 
+  public static String SUPER_USER_ID_IN_STR = "0";
+
   public static final TSStatus SUCCEED = new 
TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
 
   public static final String ONLY_ADMIN_ALLOWED =
@@ -336,7 +340,7 @@ public class AuthorityChecker {
 
     List<ColumnHeader> headerList = new ArrayList<>();
     TsBlockBuilder builder;
-    if (listRoleUser) {
+    if (authResp.tag.equals(ColumnHeaderConstant.ROLE)) {
       headerList.add(new ColumnHeader(authResp.getTag(), TSDataType.TEXT));
       types.add(TSDataType.TEXT);
       builder = new TsBlockBuilder(types);
@@ -345,6 +349,22 @@ public class AuthorityChecker {
         builder.getColumnBuilder(0).writeBinary(new Binary(name, 
TSFileConfig.STRING_CHARSET));
         builder.declarePosition();
       }
+    } else if (authResp.tag.equals(ColumnHeaderConstant.USER)) {
+      headerList = LIST_USER_COLUMN_HEADERS;
+      types =
+          LIST_USER_COLUMN_HEADERS.stream()
+              .map(ColumnHeader::getColumnType)
+              .collect(Collectors.toList());
+      builder = new TsBlockBuilder(types);
+      for (TListUserInfo userinfo : authResp.getUsersInfo()) {
+        builder.getTimeColumnBuilder().writeLong(0L);
+        builder.getColumnBuilder(0).writeLong(userinfo.getUserId());
+        builder
+            .getColumnBuilder(1)
+            .writeBinary(new Binary(userinfo.getUsername(), 
TSFileConfig.STRING_CHARSET));
+        builder.declarePosition();
+      }
+
     } else {
       headerList = LIST_USER_OR_ROLE_PRIVILEGES_COLUMN_HEADERS;
       types =
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/UserTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/UserTest.java
index eee3be0bc73..82e7d8f15b1 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/UserTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/UserTest.java
@@ -39,13 +39,13 @@ public class UserTest {
     user.setPathPrivileges(
         new PartialPath("root.ln"), 
Collections.singleton(PrivilegeType.WRITE_DATA));
     Assert.assertEquals(
-        "User{name='user', pathPrivilegeList=[root.ln : WRITE_DATA], "
+        "User{id=-1, name='user', pathPrivilegeList=[root.ln : WRITE_DATA], "
             + "sysPrivilegeSet=[], AnyScopePrivilegeMap=[], 
objectPrivilegeMap={}, roleList=[], isOpenIdUser=false}",
         user.toString());
     User user1 = new User("user1", "password1");
     user1.deserialize(user.serialize());
     Assert.assertEquals(
-        "User{name='user', pathPrivilegeList=[root.ln : WRITE_DATA], "
+        "User{id=-1, name='user', pathPrivilegeList=[root.ln : WRITE_DATA], "
             + "sysPrivilegeSet=[], AnyScopePrivilegeMap=[], 
objectPrivilegeMap={}, roleList=[], isOpenIdUser=false}",
         user1.toString());
     Assert.assertEquals(user1, user);
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserAccessorTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserAccessorTest.java
index 54442238891..eafb95b0824 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserAccessorTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserAccessorTest.java
@@ -66,6 +66,7 @@ public class LocalFileUserAccessorTest {
   @Test
   public void test() throws IOException, IllegalPathException {
     User user = new User("test", "password123456");
+    user.setUserId(5);
     user.grantSysPrivilege(PrivilegeType.EXTEND_TEMPLATE, false);
     user.grantSysPrivilege(PrivilegeType.MANAGE_USER, false);
     PathPrivilege pathPrivilege = new PathPrivilege(new 
PartialPath("root.test"));
@@ -80,23 +81,24 @@ public class LocalFileUserAccessorTest {
     user.addRole("testRole2");
     accessor.saveEntity(user);
     accessor.reset();
-    User loadUser = accessor.loadEntity("test");
+    User loadUser = accessor.loadEntity("5");
     assertEquals(user, loadUser);
     user.setName("test1");
+    user.setUserId(6);
     accessor.saveEntity(user);
 
     // list
     List<String> usernames = accessor.listAllEntities();
     usernames.sort(null);
-    assertTrue(usernames.contains("test"));
-    assertTrue(usernames.contains("test1"));
+    assertTrue(usernames.contains("5"));
+    assertTrue(usernames.contains("6"));
 
     // delete
     assertFalse(accessor.deleteEntity("not a user"));
-    assertTrue(accessor.deleteEntity(user.getName()));
+    assertTrue(accessor.deleteEntity(String.valueOf(user.getUserId())));
     usernames = accessor.listAllEntities();
     assertEquals(1, usernames.size());
-    assertTrue(usernames.contains("test"));
+    assertTrue(usernames.contains("5"));
     User nullUser = accessor.loadEntity(user.getName());
     assertNull(nullUser);
   }
@@ -127,9 +129,14 @@ public class LocalFileUserAccessorTest {
     accessor.saveUserOldVersion(role);
     User newRole = accessor.loadEntity("root");
     assertEquals(role, newRole);
+    newRole.setName("root1");
+    accessor.saveUserOldVersion1(newRole);
+    User newRole1 = accessor.loadEntity("root1");
+    assertEquals(newRole, newRole1);
     newRole.setName("root2");
+    newRole.setUserId(10000);
     accessor.saveEntity(newRole);
-    User newRole2 = accessor.loadEntity("root2");
+    User newRole2 = accessor.loadEntity("10000");
     assertEquals(newRole, newRole2);
   }
 }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java
index 6f373dd49ea..2c4ac064599 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java
@@ -32,6 +32,7 @@ import 
org.apache.iotdb.commons.security.encrypt.AsymmetricEncrypt;
 import org.apache.iotdb.commons.service.IService;
 import org.apache.iotdb.commons.service.ServiceType;
 import org.apache.iotdb.commons.utils.AuthUtils;
+import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo;
 import org.apache.iotdb.rpc.TSStatusCode;
 
 import org.apache.thrift.TException;
@@ -453,6 +454,11 @@ public abstract class BasicAuthorizer implements 
IAuthorizer, IService {
     return userManager.listAllEntities();
   }
 
+  @Override
+  public List<TListUserInfo> listAllUsersInfo() {
+    return userManager.listAllEntitiesInfo();
+  }
+
   @Override
   public List<String> listAllRoles() {
     return roleManager.listAllEntities();
@@ -468,6 +474,11 @@ public abstract class BasicAuthorizer implements 
IAuthorizer, IService {
     return userManager.getEntity(username);
   }
 
+  @Override
+  public User getUser(long userId) throws AuthException {
+    return userManager.getEntity(userId);
+  }
+
   @Override
   public boolean processTakeSnapshot(File snapshotDir) throws TException, 
IOException {
     return userManager.processTakeSnapshot(snapshotDir)
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java
index 82143345ba5..f00d28ccade 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.commons.auth.entity.Role;
 import org.apache.iotdb.commons.auth.entity.User;
 import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.snapshot.SnapshotProcessor;
+import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo;
 
 import java.util.List;
 import java.util.Map;
@@ -184,6 +185,14 @@ public interface IAuthorizer extends SnapshotProcessor {
    */
   List<String> listAllUsers();
 
+  /**
+   * List existing users info in the database.
+   *
+   * @return A list contains all users' baisc info including userid, 
username,maxSessionPerUser and
+   *     minSessionPerUser.
+   */
+  List<TListUserInfo> listAllUsersInfo();
+
   /**
    * List existing roles in the database.
    *
@@ -207,6 +216,14 @@ public interface IAuthorizer extends SnapshotProcessor {
    */
   User getUser(String username) throws AuthException;
 
+  /**
+   * Find a user by its userId.
+   *
+   * @param userId the index of the user.
+   * @return A user whose id is userId or null if such user does not exist.
+   */
+  User getUser(long userId) throws AuthException;
+
   /**
    * get all user
    *
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java
index 3c42d1a1dec..972106467e2 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java
@@ -35,6 +35,21 @@ public interface IEntityAccessor extends SnapshotProcessor {
    */
   Role loadEntity(String entityName) throws IOException;
 
+  /**
+   * Deserialize userid from lower storage.
+   *
+   * @return The max userid.
+   * @throws IOException if an exception is raised when interacting with the 
lower storage.
+   */
+  long loadUserId() throws IOException;
+
+  /**
+   * save maxUserId to lower storage when snapshot.
+   *
+   * @throws IOException if an exception is raised when interacting with the 
lower storage.
+   */
+  void saveUserId(long nextUserId) throws IOException;
+
   /**
    * Serialize the entity object to lower storage.
    *
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java
index 21481de80c4..f54ed493af1 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java
@@ -44,6 +44,8 @@ import java.util.Set;
 public class Role {
 
   protected String name;
+  protected int maxSessionPerUser = -1;
+  protected int minSessionPerUser = -1;
   protected List<PathPrivilege> pathPrivilegeList;
 
   protected Map<String, DatabasePrivilege> objectPrivilegeMap;
@@ -81,6 +83,14 @@ public class Role {
     return name;
   }
 
+  public int getMaxSessionPerUser() {
+    return maxSessionPerUser;
+  }
+
+  public int getMinSessionPerUser() {
+    return minSessionPerUser;
+  }
+
   public List<PathPrivilege> getPathPrivilegeList() {
     return pathPrivilegeList;
   }
@@ -250,6 +260,14 @@ public class Role {
     this.name = name;
   }
 
+  public void setMaxSessionPerUser(int maxSessionPerUser) {
+    this.maxSessionPerUser = maxSessionPerUser;
+  }
+
+  public void setMinSessionPerUser(int minSessionPerUser) {
+    this.minSessionPerUser = minSessionPerUser;
+  }
+
   public void setPrivilegeList(List<PathPrivilege> privilegeList) {
     this.pathPrivilegeList = privilegeList;
   }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java
index c0098cf0d51..d7963bc3d77 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java
@@ -19,6 +19,8 @@
 package org.apache.iotdb.commons.auth.entity;
 
 import org.apache.iotdb.commons.utils.SerializeUtils;
+import org.apache.iotdb.commons.utils.TestOnly;
+import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo;
 import org.apache.iotdb.confignode.rpc.thrift.TUserResp;
 
 import java.io.ByteArrayOutputStream;
@@ -34,6 +36,8 @@ import java.util.Set;
 /** This class contains all information of a User. */
 public class User extends Role {
 
+  private long userId = -1;
+
   private String password;
 
   private Set<String> roleSet;
@@ -44,15 +48,24 @@ public class User extends Role {
     // empty constructor
   }
 
+  @TestOnly
+  public User(String name, String password) {
+    super(name);
+    this.password = password;
+    this.roleSet = new HashSet<>();
+  }
+
   /**
    * construct function for User.
    *
    * @param name -user name
    * @param password -user password
+   * @param userId -user index
    */
-  public User(String name, String password) {
+  public User(String name, String password, long userId) {
     super(name);
     this.password = password;
+    this.userId = userId;
     this.roleSet = new HashSet<>();
   }
 
@@ -73,7 +86,15 @@ public class User extends Role {
     roleSet.add(roleName);
   }
 
+  public void setUserId(long userId) {
+    this.userId = userId;
+  }
+
   /** ------------ get func ----------------* */
+  public long getUserId() {
+    return userId;
+  }
+
   public String getPassword() {
     return password;
   }
@@ -99,6 +120,15 @@ public class User extends Role {
     return resp;
   }
 
+  public TListUserInfo convertToListUserInfo() {
+    TListUserInfo userInfo = new TListUserInfo();
+    userInfo.setUserId(userId);
+    userInfo.setUsername(name);
+    userInfo.setMaxSessionPerUser(maxSessionPerUser);
+    userInfo.setMinSessionPerUser(minSessionPerUser);
+    return userInfo;
+  }
+
   /** -------------- misc ----------------* */
   @Override
   public boolean equals(Object o) {
@@ -196,7 +226,9 @@ public class User extends Role {
   @Override
   public String toString() {
     return "User{"
-        + "name='"
+        + "id="
+        + userId
+        + ", name='"
         + super.getName()
         + '\''
         + ", pathPrivilegeList="
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java
index 17cda526d34..f6804d3055c 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java
@@ -23,9 +23,11 @@ import org.apache.iotdb.commons.auth.entity.IEntityAccessor;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.auth.entity.PrivilegeUnion;
 import org.apache.iotdb.commons.auth.entity.Role;
+import org.apache.iotdb.commons.auth.entity.User;
 import org.apache.iotdb.commons.concurrent.HashLock;
 import org.apache.iotdb.commons.snapshot.SnapshotProcessor;
 import org.apache.iotdb.commons.utils.AuthUtils;
+import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo;
 import org.apache.iotdb.rpc.TSStatusCode;
 
 import org.slf4j.Logger;
@@ -33,6 +35,7 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -78,6 +81,10 @@ public abstract class BasicRoleManager implements 
IEntityManager, SnapshotProces
     return role;
   }
 
+  public Role getEntity(long entityId) {
+    return null;
+  }
+
   public boolean createRole(String entityName) {
     Role role = getEntity(entityName);
     if (role != null) {
@@ -229,4 +236,19 @@ public abstract class BasicRoleManager implements 
IEntityManager, SnapshotProces
     rtlist.sort(null);
     return rtlist;
   }
+
+  public List<TListUserInfo> listAllEntitiesInfo() {
+
+    List<TListUserInfo> rtlist = new ArrayList<>();
+    for (Role r : entityMap.values()) {
+      TListUserInfo userInfo = new TListUserInfo();
+      userInfo.userId = ((User) r).getUserId();
+      userInfo.username = r.getName();
+      userInfo.maxSessionPerUser = r.getMaxSessionPerUser();
+      userInfo.minSessionPerUser = r.getMinSessionPerUser();
+      rtlist.add(userInfo);
+    }
+    rtlist.sort(Comparator.comparingLong(TListUserInfo::getUserId));
+    return rtlist;
+  }
 }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IEntityManager.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IEntityManager.java
index d70c18273f0..c1331fa8483 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IEntityManager.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IEntityManager.java
@@ -37,6 +37,15 @@ public interface IEntityManager extends SnapshotProcessor {
    */
   Role getEntity(String entityName) throws AuthException;
 
+  /**
+   * Get an entity object.
+   *
+   * @param entityId The id of the entity.
+   * @return An entity object whose index is entityId or null if such entity 
does not exist.
+   * @throws AuthException if exception is raised while getting the entity.
+   */
+  Role getEntity(long entityId) throws AuthException;
+
   /**
    * Delete an entity.
    *
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java
index 809d2397f3b..07bb08ca7de 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java
@@ -22,6 +22,7 @@ import org.apache.iotdb.commons.auth.entity.DatabasePrivilege;
 import org.apache.iotdb.commons.auth.entity.IEntityAccessor;
 import org.apache.iotdb.commons.auth.entity.PathPrivilege;
 import org.apache.iotdb.commons.auth.entity.Role;
+import org.apache.iotdb.commons.auth.entity.User;
 import org.apache.iotdb.commons.conf.CommonDescriptor;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.exception.IllegalPathException;
@@ -84,7 +85,7 @@ public class LocalFileRoleAccessor implements IEntityAccessor 
{
 
   // It might be a good idea to use a Version number to control upgrade 
compatibility.
   // Now it's version 1
-  protected static final int VERSION = 1;
+  protected static final int VERSION = 2;
 
   /**
    * Reused buffer for primitive types encoding/decoding, which aim to reduce 
memory fragments. Use
@@ -223,13 +224,37 @@ public class LocalFileRoleAccessor implements 
IEntityAccessor {
     }
   }
 
+  @Override
+  public long loadUserId() throws IOException {
+    File userIdFile = checkFileAvailable("user_id", "");
+    if (userIdFile == null) {
+      return -1;
+    }
+    FileInputStream inputStream = new FileInputStream(userIdFile);
+    try (DataInputStream dataInputStream =
+        new DataInputStream(new BufferedInputStream(inputStream))) {
+      dataInputStream.readInt(); // read version
+      return dataInputStream.readLong();
+    } catch (Exception e) {
+      throw new IOException(e);
+    } finally {
+      strBufferLocal.remove();
+    }
+  }
+
   @Override
   public void saveEntity(Role entity) throws IOException {
+    String prefixName = "";
+    if (entity instanceof User) {
+      prefixName = String.valueOf(((User) entity).getUserId());
+    } else {
+      prefixName = entity.getName();
+    }
     File roleProfile =
         SystemFileFactory.INSTANCE.getFile(
             entityDirPath
                 + File.separator
-                + entity.getName()
+                + prefixName
                 + IoTDBConstant.PROFILE_SUFFIX
                 + TEMP_SUFFIX);
     File roleDir = new File(entityDirPath);
@@ -253,7 +278,7 @@ public class LocalFileRoleAccessor implements 
IEntityAccessor {
 
     File oldFile =
         SystemFileFactory.INSTANCE.getFile(
-            entityDirPath + File.separator + entity.getName() + 
IoTDBConstant.PROFILE_SUFFIX);
+            entityDirPath + File.separator + prefixName + 
IoTDBConstant.PROFILE_SUFFIX);
     IOUtils.replaceFile(roleProfile, oldFile);
     saveRoles(entity);
   }
@@ -301,6 +326,7 @@ public class LocalFileRoleAccessor implements 
IEntityAccessor {
       }
       retList.addAll(set);
     }
+    retList.remove("user_id"); // skip user_id.profile
     return retList;
   }
 
@@ -378,4 +404,36 @@ public class LocalFileRoleAccessor implements 
IEntityAccessor {
       LOGGER.warn("Role folder not exists");
     }
   }
+
+  @Override
+  public void saveUserId(long nextUserId) throws IOException {
+    File userInfoProfile =
+        SystemFileFactory.INSTANCE.getFile(
+            entityDirPath
+                + File.separator
+                + "user_id"
+                + IoTDBConstant.PROFILE_SUFFIX
+                + TEMP_SUFFIX);
+    File userDir = new File(entityDirPath);
+    if (!userDir.exists() && !userDir.mkdirs()) {
+      LOGGER.error("Failed to create user dir {}", entityDirPath);
+    }
+
+    try (FileOutputStream fileOutputStream = new 
FileOutputStream(userInfoProfile);
+        BufferedOutputStream outputStream = new 
BufferedOutputStream(fileOutputStream)) {
+      IOUtils.writeInt(outputStream, VERSION, encodingBufferLocal);
+      IOUtils.writeLong(outputStream, nextUserId, encodingBufferLocal);
+      outputStream.flush();
+      fileOutputStream.getFD().sync();
+    } catch (Exception e) {
+      LOGGER.warn("meet error when save userId: {}", nextUserId);
+      throw new IOException(e);
+    } finally {
+      encodingBufferLocal.remove();
+    }
+    File oldFile =
+        SystemFileFactory.INSTANCE.getFile(
+            entityDirPath + File.separator + "user_id" + 
IoTDBConstant.PROFILE_SUFFIX);
+    IOUtils.replaceFile(userInfoProfile, oldFile);
+  }
 }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java
index 296cd0fa6a4..0253e3dab0f 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java
@@ -22,6 +22,7 @@ import org.apache.iotdb.commons.auth.AuthException;
 import org.apache.iotdb.commons.auth.entity.IEntityAccessor;
 import org.apache.iotdb.commons.auth.entity.PathPrivilege;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
+import org.apache.iotdb.commons.auth.entity.Role;
 import org.apache.iotdb.commons.auth.entity.User;
 import org.apache.iotdb.commons.auth.role.BasicRoleManager;
 import org.apache.iotdb.commons.conf.CommonDescriptor;
@@ -35,6 +36,9 @@ import org.apache.iotdb.rpc.TSStatusCode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.util.Map;
+
 /** This class stores information of each user. */
 public abstract class BasicUserManager extends BasicRoleManager {
 
@@ -50,6 +54,8 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
     return "No such user %s";
   }
 
+  protected long nextUserId = 9999;
+
   /**
    * BasicUserManager Constructor.
    *
@@ -99,11 +105,42 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
     LOGGER.info("Admin initialized");
   }
 
+  private void initUserId() {
+    try {
+      long maxUserId = this.accessor.loadUserId();
+      if (maxUserId < 9999) {
+        nextUserId = 9999;
+      } else {
+        nextUserId = maxUserId;
+      }
+
+      for (Map.Entry<String, Role> userEntry : entityMap.entrySet()) {
+        User user = (User) userEntry.getValue();
+        if (user.getUserId() == -1) {
+          user.setUserId(++nextUserId);
+        }
+      }
+    } catch (IOException e) {
+      LOGGER.warn("meet error in load max userId.", e);
+      throw new RuntimeException(e);
+    }
+  }
+
   @Override
   public User getEntity(String entityName) {
     return (User) super.getEntity(entityName);
   }
 
+  @Override
+  public User getEntity(long entityId) {
+    for (Map.Entry<String, Role> roleEntry : entityMap.entrySet()) {
+      if (((User) roleEntry.getValue()).getUserId() == entityId) {
+        return (User) roleEntry.getValue();
+      }
+    }
+    return null;
+  }
+
   public boolean createUser(
       String username, String password, boolean validCheck, boolean 
enableEncrypt)
       throws AuthException {
@@ -125,7 +162,15 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
     }
     lock.writeLock(username);
     try {
-      user = new User(username, enableEncrypt ? 
AuthUtils.encryptPassword(password) : password);
+      long userid;
+      if 
(username.equals(CommonDescriptor.getInstance().getConfig().getAdminName())) {
+        userid = 0;
+      } else {
+        userid = ++nextUserId;
+      }
+      user =
+          new User(
+              username, enableEncrypt ? AuthUtils.encryptPassword(password) : 
password, userid);
       entityMap.put(username, user);
       return true;
     } finally {
@@ -193,7 +238,18 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
 
   @Override
   public void reset() throws AuthException {
-    super.reset();
+    accessor.reset();
+    entityMap.clear();
+    for (String userId : accessor.listAllEntities()) {
+      try {
+        User user = (User) accessor.loadEntity(userId);
+        entityMap.put(user.getName(), user);
+      } catch (IOException e) {
+        LOGGER.warn("Get exception when load user {}", userId);
+        throw new AuthException(TSStatusCode.AUTH_IO_EXCEPTION, e);
+      }
+    }
+    initUserId();
     initAdmin();
   }
 
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
index 8a50a87ad96..11ff8c73e9a 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java
@@ -89,6 +89,7 @@ public class LocalFileUserAccessor extends 
LocalFileRoleAccessor {
 
   @Override
   protected void saveEntityName(BufferedOutputStream outputStream, Role role) 
throws IOException {
+    IOUtils.writeLong(outputStream, ((User) role).getUserId(), 
encodingBufferLocal);
     super.saveEntityName(outputStream, role);
     IOUtils.writeString(
         outputStream, ((User) role).getPassword(), STRING_ENCODING, 
encodingBufferLocal);
@@ -101,7 +102,7 @@ public class LocalFileUserAccessor extends 
LocalFileRoleAccessor {
         SystemFileFactory.INSTANCE.getFile(
             entityDirPath
                 + File.separator
-                + user.getName()
+                + user.getUserId()
                 + ROLE_SUFFIX
                 + IoTDBConstant.PROFILE_SUFFIX
                 + TEMP_SUFFIX);
@@ -123,7 +124,7 @@ public class LocalFileUserAccessor extends 
LocalFileRoleAccessor {
         SystemFileFactory.INSTANCE.getFile(
             entityDirPath
                 + File.separator
-                + user.getName()
+                + user.getUserId()
                 + ROLE_SUFFIX
                 + IoTDBConstant.PROFILE_SUFFIX);
     IOUtils.replaceFile(roleProfile, oldURoleFile);
@@ -144,14 +145,10 @@ public class LocalFileUserAccessor extends 
LocalFileRoleAccessor {
     FileInputStream inputStream = new FileInputStream(entityFile);
     try (DataInputStream dataInputStream =
         new DataInputStream(new BufferedInputStream(inputStream))) {
-      boolean fromOldVersion = false;
       int tag = dataInputStream.readInt();
-      if (tag < 0) {
-        fromOldVersion = true;
-      }
       User user = new User();
 
-      if (fromOldVersion) {
+      if (tag < 0) {
         String name =
             IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal, -1 * tag);
         user.setName(name);
@@ -163,8 +160,13 @@ public class LocalFileUserAccessor extends 
LocalFileRoleAccessor {
               IOUtils.readPathPrivilege(dataInputStream, STRING_ENCODING, 
strBufferLocal));
         }
         user.setPrivilegeList(pathPrivilegeList);
+      } else if (tag == 1) {
+        user.setName(IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal));
+        user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal));
+        loadPrivileges(dataInputStream, user);
       } else {
         assert (tag == VERSION);
+        user.setUserId(dataInputStream.readLong());
         user.setName(IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal));
         user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, 
strBufferLocal));
         loadPrivileges(dataInputStream, user);
@@ -300,4 +302,35 @@ public class LocalFileUserAccessor extends 
LocalFileRoleAccessor {
             entityDirPath + File.separator + user.getName() + 
IoTDBConstant.PROFILE_SUFFIX);
     IOUtils.replaceFile(userProfile, oldFile);
   }
+
+  @TestOnly
+  public void saveUserOldVersion1(User user) throws IOException {
+    File userProfile =
+        SystemFileFactory.INSTANCE.getFile(
+            entityDirPath
+                + File.separator
+                + user.getName()
+                + IoTDBConstant.PROFILE_SUFFIX
+                + TEMP_SUFFIX);
+
+    try (FileOutputStream fileOutputStream = new FileOutputStream(userProfile);
+        BufferedOutputStream outputStream = new 
BufferedOutputStream(fileOutputStream)) {
+      // test for version1
+      IOUtils.writeInt(outputStream, 1, encodingBufferLocal);
+      IOUtils.writeString(outputStream, user.getName(), STRING_ENCODING, 
encodingBufferLocal);
+      IOUtils.writeString(outputStream, user.getPassword(), STRING_ENCODING, 
encodingBufferLocal);
+      savePrivileges(outputStream, user);
+      outputStream.flush();
+      fileOutputStream.getFD().sync();
+    } catch (Exception e) {
+      throw new IOException(e);
+    } finally {
+      encodingBufferLocal.remove();
+    }
+
+    File oldFile =
+        SystemFileFactory.INSTANCE.getFile(
+            entityDirPath + File.separator + user.getName() + 
IoTDBConstant.PROFILE_SUFFIX);
+    IOUtils.replaceFile(userProfile, oldFile);
+  }
 }
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java
index e2c8a33fee0..5b061e16ba2 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java
@@ -39,6 +39,7 @@ public class LocalFileUserManager extends BasicUserManager {
     for (Map.Entry<String, Role> entry : entityMap.entrySet()) {
       accessor.saveEntity(entry.getValue());
     }
+    accessor.saveUserId(nextUserId);
     return accessor.processTakeSnapshot(snapshotDir);
   }
 
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
index 89749a491ac..145cb7774fa 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
@@ -163,6 +163,8 @@ public class ColumnHeaderConstant {
   public static final String COUNT_TIME_PARTITION = "count(timePartition)";
   public static final String START_TIME = "StartTime";
   public static final String ROLE = "Role";
+  public static final String MAX_SESSION_PER_USER = "MaxSessionPerUser";
+  public static final String MIN_SESSION_PER_USER = "MinSessionPerUser";
   public static final String CREATE_TIME = "CreateTime";
   public static final String TSFILE_SIZE = "TsFileSize";
   public static final String COMPRESSION_RATIO = "CompressionRatio";
@@ -279,6 +281,7 @@ public class ColumnHeaderConstant {
 
   // column names for show throttle quota
   public static final String USER = "User";
+  public static final String USER_ID = "UserId";
   public static final String READ_WRITE = "Read/Write";
 
   // column names for show models/trials
@@ -684,6 +687,10 @@ public class ColumnHeaderConstant {
           new ColumnHeader(TABLE, TSDataType.TEXT),
           new ColumnHeader(CREATE_TABLE, TSDataType.TEXT));
 
+  public static final List<ColumnHeader> LIST_USER_COLUMN_HEADERS =
+      ImmutableList.of(
+          new ColumnHeader(USER_ID, TSDataType.INT64), new ColumnHeader(USER, 
TSDataType.TEXT));
+
   public static final List<ColumnHeader> showTablesColumnHeaders =
       ImmutableList.of(
           new ColumnHeader(TABLE_NAME, TSDataType.TEXT),
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java
index d1c049e86d7..8b63d29bd78 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java
@@ -92,6 +92,36 @@ public class IOUtils {
     outputStream.write(encodingBuffer.array(), 0, Integer.BYTES);
   }
 
+  /**
+   * Write a long (8-byte) into the given stream.
+   *
+   * @param outputStream the destination to insert.
+   * @param i the long value to be written.
+   * @param encodingBufferLocal a ThreadLocal buffer may be passed to avoid 
frequent memory
+   *     allocations. A null may also be passed to use a local buffer.
+   * @throws IOException when an exception raised during operating the stream.
+   */
+  public static void writeLong(
+      OutputStream outputStream, long i, ThreadLocal<ByteBuffer> 
encodingBufferLocal)
+      throws IOException {
+
+    ByteBuffer encodingBuffer;
+    if (encodingBufferLocal != null) {
+      encodingBuffer = encodingBufferLocal.get();
+      if (encodingBuffer == null) {
+        // 8 bytes is exactly what we need for a long
+        encodingBuffer = ByteBuffer.allocate(8);
+        encodingBufferLocal.set(encodingBuffer);
+      }
+    } else {
+      encodingBuffer = ByteBuffer.allocate(8);
+    }
+
+    encodingBuffer.clear();
+    encodingBuffer.putLong(i);
+    outputStream.write(encodingBuffer.array(), 0, Long.BYTES);
+  }
+
   /**
    * Read a string from the given stream.
    *
diff --git a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift 
b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
index 37f60c9a206..aa3c312bdd0 100644
--- a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
+++ b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift
@@ -359,6 +359,14 @@ struct TAuthorizerResp {
   2: optional string tag
   3: optional list<string> memberInfo
   4: optional TPermissionInfoResp permissionInfo
+  5: optional list<TListUserInfo> usersInfo
+}
+
+struct TListUserInfo{
+ 1: required i64 userId
+ 2: required string username
+ 3: required i32 maxSessionPerUser
+ 4: required i32 minSessionPerUser
 }
 
 struct TUserResp {

Reply via email to