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

wangchao316 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 21c530abf7 [IOTDB-4007]Support multiple paths in privilege control 
(#6854)
21c530abf7 is described below

commit 21c530abf75cb3825c3724fc1c4e00cb5f8013a4
Author: Yifu Zhou <[email protected]>
AuthorDate: Tue Aug 2 15:11:58 2022 +0800

    [IOTDB-4007]Support multiple paths in privilege control (#6854)
    
    [IOTDB-4007]Support multiple paths in privilege control (#6854)
---
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   |  12 +-
 .../consensus/request/auth/AuthorPlan.java         |  25 +-
 .../iotdb/confignode/persistence/AuthorInfo.java   |  68 +++--
 .../thrift/ConfigNodeRPCServiceProcessor.java      |   4 +-
 .../request/ConfigPhysicalPlanSerDeTest.java       | 121 ++++++--
 .../confignode/persistence/AuthorInfoTest.java     | 305 +++++++++++++++++++--
 .../thrift/ConfigNodeRPCServiceProcessorTest.java  |  74 +++--
 .../Administration-Management/Administration.md    |  38 +--
 .../Administration-Management/Administration.md    |  38 +--
 .../commons/utils/BasicStructureSerDeUtil.java     |  31 +++
 .../iotdb/db/auth/ClusterAuthorityFetcher.java     |   9 +-
 .../iotdb/db/localconfignode/LocalConfigNode.java  |  73 +++--
 .../iotdb/db/mpp/plan/parser/ASTVisitor.java       |  48 +++-
 .../db/mpp/plan/statement/sys/AuthorStatement.java |  12 +-
 .../apache/iotdb/db/qp/executor/PlanExecutor.java  |  74 +++--
 .../iotdb/db/qp/logical/sys/AuthorOperator.java    |  14 +-
 .../iotdb/db/qp/physical/sys/AuthorPlan.java       |  54 ++--
 .../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java    |  44 ++-
 .../iotdb/db/qp/physical/PhysicalPlanTest.java     |   2 +-
 .../src/main/thrift/confignode.thrift              |   2 +-
 20 files changed, 814 insertions(+), 234 deletions(-)

diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 
b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
index 73e762f051..410f32d388 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
@@ -530,12 +530,12 @@ alterUser
 
 // Grant User Privileges
 grantUser
-    : GRANT USER userName=identifier PRIVILEGES privileges (ON prefixPath)?
+    : GRANT USER userName=identifier PRIVILEGES privileges (ON prefixPath 
(COMMA prefixPath)*)?
     ;
 
 // Grant Role Privileges
 grantRole
-    : GRANT ROLE roleName=identifier PRIVILEGES privileges ON prefixPath
+    : GRANT ROLE roleName=identifier PRIVILEGES privileges ON prefixPath 
(COMMA prefixPath)*
     ;
 
 // Grant User Role
@@ -545,12 +545,12 @@ grantRoleToUser
 
 // Revoke User Privileges
 revokeUser
-    : REVOKE USER userName=identifier PRIVILEGES privileges (ON prefixPath)?
+    : REVOKE USER userName=identifier PRIVILEGES privileges (ON prefixPath 
(COMMA prefixPath)*)?
     ;
 
 // Revoke Role Privileges
 revokeRole
-    : REVOKE ROLE roleName=identifier PRIVILEGES privileges ON prefixPath
+    : REVOKE ROLE roleName=identifier PRIVILEGES privileges ON prefixPath 
(COMMA prefixPath)*
     ;
 
 // Revoke Role From User
@@ -580,12 +580,12 @@ listRole
 
 // List Privileges
 listPrivilegesUser
-    : LIST PRIVILEGES USER userName=usernameWithRoot ON prefixPath
+    : LIST PRIVILEGES USER userName=usernameWithRoot ON prefixPath (COMMA 
prefixPath)*
     ;
 
 // List Privileges of Roles On Specific Path
 listPrivilegesRole
-    : LIST PRIVILEGES ROLE roleName=identifier ON prefixPath
+    : LIST PRIVILEGES ROLE roleName=identifier ON prefixPath (COMMA 
prefixPath)*
     ;
 
 // List Privileges of Users
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/auth/AuthorPlan.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/auth/AuthorPlan.java
index 754f1e00ce..9684b864d6 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/auth/AuthorPlan.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/auth/AuthorPlan.java
@@ -27,6 +27,7 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -37,7 +38,7 @@ public class AuthorPlan extends ConfigPhysicalPlan {
   private String password;
   private String newPassword;
   private Set<Integer> permissions;
-  private String nodeName;
+  private List<String> nodeNameList;
   private String userName;
 
   public AuthorPlan(ConfigPhysicalPlanType type) {
@@ -54,7 +55,7 @@ public class AuthorPlan extends ConfigPhysicalPlan {
    * @param password password
    * @param newPassword new password
    * @param permissions permissions
-   * @param nodeName node name in Path structure
+   * @param nodeNameList node name in Path structure
    * @throws AuthException Authentication Exception
    */
   public AuthorPlan(
@@ -64,7 +65,7 @@ public class AuthorPlan extends ConfigPhysicalPlan {
       String password,
       String newPassword,
       Set<Integer> permissions,
-      String nodeName)
+      List<String> nodeNameList)
       throws AuthException {
     this(authorType);
     this.authorType = authorType;
@@ -73,7 +74,7 @@ public class AuthorPlan extends ConfigPhysicalPlan {
     this.password = password;
     this.newPassword = newPassword;
     this.permissions = permissions;
-    this.nodeName = nodeName;
+    this.nodeNameList = nodeNameList;
   }
 
   public ConfigPhysicalPlanType getAuthorType() {
@@ -116,12 +117,12 @@ public class AuthorPlan extends ConfigPhysicalPlan {
     this.permissions = permissions;
   }
 
-  public String getNodeName() {
-    return nodeName;
+  public List<String> getNodeNameList() {
+    return nodeNameList;
   }
 
-  public void setNodeName(String nodeName) {
-    this.nodeName = nodeName;
+  public void setNodeNameList(List<String> nodeNameList) {
+    this.nodeNameList = nodeNameList;
   }
 
   public String getUserName() {
@@ -148,7 +149,7 @@ public class AuthorPlan extends ConfigPhysicalPlan {
         stream.writeInt(permission);
       }
     }
-    BasicStructureSerDeUtil.write(nodeName, stream);
+    BasicStructureSerDeUtil.write(nodeNameList, stream);
   }
 
   @Override
@@ -167,7 +168,7 @@ public class AuthorPlan extends ConfigPhysicalPlan {
         permissions.add(buffer.getInt());
       }
     }
-    nodeName = BasicStructureSerDeUtil.readString(buffer);
+    nodeNameList = BasicStructureSerDeUtil.readStringList(buffer);
   }
 
   private int getPlanTypeOrdinal(ConfigPhysicalPlanType 
configPhysicalPlanType) {
@@ -245,12 +246,12 @@ public class AuthorPlan extends ConfigPhysicalPlan {
         && Objects.equals(password, that.password)
         && Objects.equals(newPassword, that.newPassword)
         && Objects.equals(permissions, that.permissions)
-        && Objects.equals(nodeName, that.nodeName);
+        && Objects.equals(nodeNameList, that.nodeNameList);
   }
 
   @Override
   public int hashCode() {
     return Objects.hash(
-        authorType, userName, roleName, password, newPassword, permissions, 
nodeName);
+        authorType, userName, roleName, password, newPassword, permissions, 
nodeNameList);
   }
 }
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java
index e034bb2f2e..0edaf346a2 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java
@@ -50,6 +50,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -145,7 +146,7 @@ public class AuthorInfo implements SnapshotProcessor {
     String password = authorPlan.getPassword();
     String newPassword = authorPlan.getNewPassword();
     Set<Integer> permissions = authorPlan.getPermissions();
-    String nodeName = authorPlan.getNodeName();
+    List<String> nodeNameList = authorPlan.getNodeNameList();
     try {
       switch (authorType) {
         case UpdateUser:
@@ -165,12 +166,16 @@ public class AuthorInfo implements SnapshotProcessor {
           break;
         case GrantRole:
           for (int i : permissions) {
-            authorizer.grantPrivilegeToRole(roleName, nodeName, i);
+            for (String path : nodeNameList) {
+              authorizer.grantPrivilegeToRole(roleName, path, i);
+            }
           }
           break;
         case GrantUser:
           for (int i : permissions) {
-            authorizer.grantPrivilegeToUser(userName, nodeName, i);
+            for (String path : nodeNameList) {
+              authorizer.grantPrivilegeToUser(userName, path, i);
+            }
           }
           break;
         case GrantRoleToUser:
@@ -178,12 +183,16 @@ public class AuthorInfo implements SnapshotProcessor {
           break;
         case RevokeUser:
           for (int i : permissions) {
-            authorizer.revokePrivilegeFromUser(userName, nodeName, i);
+            for (String path : nodeNameList) {
+              authorizer.revokePrivilegeFromUser(userName, path, i);
+            }
           }
           break;
         case RevokeRole:
           for (int i : permissions) {
-            authorizer.revokePrivilegeFromRole(roleName, nodeName, i);
+            for (String path : nodeNameList) {
+              authorizer.revokePrivilegeFromRole(roleName, path, i);
+            }
           }
           break;
         case RevokeRoleFromUser:
@@ -291,15 +300,20 @@ public class AuthorInfo implements SnapshotProcessor {
     } catch (AuthException e) {
       throw new AuthException(e);
     }
-    List<String> rolePrivilegesList = new ArrayList<>();
+    Set<String> rolePrivilegesSet = new HashSet<>();
     for (PathPrivilege pathPrivilege : role.getPrivilegeList()) {
-      if (plan.getNodeName().equals("")
-          || AuthUtils.pathOrBelongsTo(plan.getNodeName(), 
pathPrivilege.getPath())) {
-        rolePrivilegesList.add(pathPrivilege.toString());
+      if (plan.getNodeNameList().isEmpty()) {
+        rolePrivilegesSet.add(pathPrivilege.toString());
+        continue;
+      }
+      for (String path : plan.getNodeNameList()) {
+        if (AuthUtils.pathOrBelongsTo(path, pathPrivilege.getPath())) {
+          rolePrivilegesSet.add(pathPrivilege.toString());
+        }
       }
     }
 
-    permissionInfo.put(IoTDBConstant.COLUMN_PRIVILEGE, rolePrivilegesList);
+    permissionInfo.put(IoTDBConstant.COLUMN_PRIVILEGE, new 
ArrayList<>(rolePrivilegesSet));
     result.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS));
     result.setPermissionInfo(permissionInfo);
     return result;
@@ -329,25 +343,45 @@ public class AuthorInfo implements SnapshotProcessor {
       }
     } else {
       List<String> rolePrivileges = new ArrayList<>();
+      Set<String> userPrivilegeSet = new HashSet<>();
       for (PathPrivilege pathPrivilege : user.getPrivilegeList()) {
-        if (plan.getNodeName().equals("")
-            || AuthUtils.pathOrBelongsTo(plan.getNodeName(), 
pathPrivilege.getPath())) {
+        if (plan.getNodeNameList().isEmpty()
+            && !userPrivilegeSet.contains(pathPrivilege.toString())) {
           rolePrivileges.add("");
-          userPrivilegesList.add(pathPrivilege.toString());
+          userPrivilegeSet.add(pathPrivilege.toString());
+          continue;
+        }
+        for (String path : plan.getNodeNameList()) {
+          if (AuthUtils.pathOrBelongsTo(path, pathPrivilege.getPath())
+              && !userPrivilegeSet.contains(pathPrivilege.toString())) {
+            rolePrivileges.add("");
+            userPrivilegeSet.add(pathPrivilege.toString());
+          }
         }
       }
+      userPrivilegesList.addAll(userPrivilegeSet);
       for (String roleN : user.getRoleList()) {
         Role role = authorizer.getRole(roleN);
         if (roleN == null) {
           continue;
         }
+        Set<String> rolePrivilegeSet = new HashSet<>();
         for (PathPrivilege pathPrivilege : role.getPrivilegeList()) {
-          if (plan.getNodeName().equals("")
-              || AuthUtils.pathOrBelongsTo(plan.getNodeName(), 
pathPrivilege.getPath())) {
-            rolePrivileges.add(roleN);
-            userPrivilegesList.add(pathPrivilege.toString());
+          if (plan.getNodeNameList().isEmpty()
+              && !rolePrivilegeSet.contains(pathPrivilege.toString())) {
+            rolePrivileges.add("");
+            rolePrivilegeSet.add(pathPrivilege.toString());
+            continue;
+          }
+          for (String path : plan.getNodeNameList()) {
+            if (AuthUtils.pathOrBelongsTo(path, pathPrivilege.getPath())
+                && !rolePrivilegeSet.contains(pathPrivilege.toString())) {
+              rolePrivileges.add(roleN);
+              rolePrivilegeSet.add(pathPrivilege.toString());
+            }
           }
         }
+        userPrivilegesList.addAll(rolePrivilegeSet);
       }
       permissionInfo.put(IoTDBConstant.COLUMN_ROLE, rolePrivileges);
     }
diff --git 
a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
 
b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
index 6c954faf66..e0cfbfbd79 100644
--- 
a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
+++ 
b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java
@@ -372,7 +372,7 @@ public class ConfigNodeRPCServiceProcessor implements 
IConfigNodeRPCService.Ifac
               req.getPassword(),
               req.getNewPassword(),
               req.getPermissions(),
-              req.getNodeName());
+              req.getNodeNameList());
     } catch (AuthException e) {
       LOGGER.error(e.getMessage());
     }
@@ -396,7 +396,7 @@ public class ConfigNodeRPCServiceProcessor implements 
IConfigNodeRPCService.Ifac
               req.getPassword(),
               req.getNewPassword(),
               req.getPermissions(),
-              req.getNodeName());
+              req.getNodeNameList());
     } catch (AuthException e) {
       LOGGER.error(e.getMessage());
     }
diff --git 
a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
 
b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
index 0bfb25764e..71cf569c19 100644
--- 
a/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
+++ 
b/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java
@@ -382,27 +382,49 @@ public class ConfigPhysicalPlanSerDeTest {
     // create user
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.CreateUser, "thulab", "", "passwd", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.CreateUser,
+            "thulab",
+            "",
+            "passwd",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // create role
     req0 =
-        new AuthorPlan(ConfigPhysicalPlanType.CreateRole, "", "admin", "", "", 
new HashSet<>(), "");
+        new AuthorPlan(
+            ConfigPhysicalPlanType.CreateRole,
+            "",
+            "admin",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // alter user
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.UpdateUser, "tempuser", "", "", "newpwd", 
new HashSet<>(), "");
+            ConfigPhysicalPlanType.UpdateUser,
+            "tempuser",
+            "",
+            "",
+            "newpwd",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // grant user
+    List<String> nodeNameList = new ArrayList<>();
+    nodeNameList.add("root.ln.**");
+    nodeNameList.add("root.abc.**");
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.GrantUser, "tempuser", "", "", "", 
permissions, "root.ln");
+            ConfigPhysicalPlanType.GrantUser, "tempuser", "", "", "", 
permissions, nodeNameList);
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
@@ -415,28 +437,34 @@ public class ConfigPhysicalPlanSerDeTest {
             "",
             "",
             permissions,
-            "root.ln");
+            nodeNameList);
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // grant role to user
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.GrantRole, "", "temprole", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.GrantRole,
+            "",
+            "temprole",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // revoke user
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.RevokeUser, "tempuser", "", "", "", 
permissions, "root.ln");
+            ConfigPhysicalPlanType.RevokeUser, "tempuser", "", "", "", 
permissions, nodeNameList);
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // revoke role
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.RevokeRole, "", "temprole", "", "", 
permissions, "root.ln");
+            ConfigPhysicalPlanType.RevokeRole, "", "temprole", "", "", 
permissions, nodeNameList);
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
@@ -449,70 +477,125 @@ public class ConfigPhysicalPlanSerDeTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // drop user
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.DropUser, "xiaoming", "", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.DropUser,
+            "xiaoming",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // drop role
     req0 =
-        new AuthorPlan(ConfigPhysicalPlanType.DropRole, "", "admin", "", "", 
new HashSet<>(), "");
+        new AuthorPlan(
+            ConfigPhysicalPlanType.DropRole,
+            "",
+            "admin",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // list user
-    req0 = new AuthorPlan(ConfigPhysicalPlanType.ListUser, "", "", "", "", new 
HashSet<>(), "");
+    req0 =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListUser, "", "", "", "", new HashSet<>(), 
new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // list role
-    req0 = new AuthorPlan(ConfigPhysicalPlanType.ListRole, "", "", "", "", new 
HashSet<>(), "");
+    req0 =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListRole, "", "", "", "", new HashSet<>(), 
new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // list privileges user
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListUserPrivilege, "", "", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListUserPrivilege,
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // list privileges role
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListRolePrivilege, "", "", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListRolePrivilege,
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // list user privileges
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListUserPrivilege, "", "", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListUserPrivilege,
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // list role privileges
     req0 =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListRolePrivilege, "", "", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListRolePrivilege,
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // list all role of user
     req0 =
-        new AuthorPlan(ConfigPhysicalPlanType.ListUserRoles, "", "", "", "", 
new HashSet<>(), "");
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListUserRoles,
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
 
     // list all user of role
     req0 =
-        new AuthorPlan(ConfigPhysicalPlanType.ListRoleUsers, "", "", "", "", 
new HashSet<>(), "");
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListRoleUsers,
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     req1 = (AuthorPlan) 
ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer());
     Assert.assertEquals(req0, req1);
   }
diff --git 
a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java
 
b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java
index c17b4301ab..f17cbb3565 100644
--- 
a/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java
+++ 
b/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java
@@ -39,10 +39,12 @@ import org.junit.Test;
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.apache.iotdb.db.constant.TestConstant.BASE_OUTPUT_PATH;
 
@@ -104,7 +106,13 @@ public class AuthorInfoTest {
     // create user
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.CreateUser, "user0", "", "passwd", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.CreateUser,
+            "user0",
+            "",
+            "passwd",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertNull(status.getMessage());
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -121,7 +129,14 @@ public class AuthorInfoTest {
 
     // drop user
     authorPlan =
-        new AuthorPlan(ConfigPhysicalPlanType.DropUser, "user1", "", "", "", 
new HashSet<>(), "");
+        new AuthorPlan(
+            ConfigPhysicalPlanType.DropUser,
+            "user1",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -135,7 +150,14 @@ public class AuthorInfoTest {
 
     // create role
     authorPlan =
-        new AuthorPlan(ConfigPhysicalPlanType.CreateRole, "", "role0", "", "", 
new HashSet<>(), "");
+        new AuthorPlan(
+            ConfigPhysicalPlanType.CreateRole,
+            "",
+            "role0",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
     authorPlan.setRoleName("role1");
@@ -144,7 +166,14 @@ public class AuthorInfoTest {
 
     // drop role
     authorPlan =
-        new AuthorPlan(ConfigPhysicalPlanType.DropRole, "", "role1", "", "", 
new HashSet<>(), "");
+        new AuthorPlan(
+            ConfigPhysicalPlanType.DropRole,
+            "",
+            "role1",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -159,14 +188,22 @@ public class AuthorInfoTest {
     // alter user
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.UpdateUser, "user0", "", "", "newpwd", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.UpdateUser,
+            "user0",
+            "",
+            "",
+            "newpwd",
+            new HashSet<>(),
+            new ArrayList<>());
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
     // grant user
+    List<String> nodeNameList = new ArrayList<>();
+    nodeNameList.add("root.ln.**");
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.GrantUser, "user0", "", "", "", 
privilegeList, "root.ln");
+            ConfigPhysicalPlanType.GrantUser, "user0", "", "", "", 
privilegeList, nodeNameList);
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -180,28 +217,34 @@ public class AuthorInfoTest {
     // grant role
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.GrantRole, "", "role0", "", "", 
privilegeList, "root.ln");
+            ConfigPhysicalPlanType.GrantRole, "", "role0", "", "", 
privilegeList, nodeNameList);
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
     // grant role to user
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.GrantRoleToUser, "user0", "role0", "", "", 
new HashSet<>(), "");
+            ConfigPhysicalPlanType.GrantRoleToUser,
+            "user0",
+            "role0",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
     // revoke user
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.RevokeUser, "user0", "", "", "", 
revokePrivilege, "root.ln");
+            ConfigPhysicalPlanType.RevokeUser, "user0", "", "", "", 
revokePrivilege, nodeNameList);
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
     // revoke role
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.RevokeRole, "", "role0", "", "", 
revokePrivilege, "root.ln");
+            ConfigPhysicalPlanType.RevokeRole, "", "role0", "", "", 
revokePrivilege, nodeNameList);
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -214,7 +257,7 @@ public class AuthorInfoTest {
             "",
             "",
             new HashSet<>(),
-            "root.ln");
+            nodeNameList);
     permissionInfoResp = authorInfo.executeListUserPrivileges(authorPlan);
     status = permissionInfoResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -224,7 +267,13 @@ public class AuthorInfoTest {
     // list user privileges
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListUserPrivilege, "user0", "", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListUserPrivilege,
+            "user0",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     permissionInfoResp = authorInfo.executeListUserPrivileges(authorPlan);
     status = permissionInfoResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -240,7 +289,7 @@ public class AuthorInfoTest {
             "",
             "",
             new HashSet<>(),
-            "root.ln");
+            nodeNameList);
     permissionInfoResp = authorInfo.executeListRolePrivileges(authorPlan);
     status = permissionInfoResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -251,7 +300,13 @@ public class AuthorInfoTest {
     // list role privileges
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListRolePrivilege, "", "role0", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListRolePrivilege,
+            "",
+            "role0",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     permissionInfoResp = authorInfo.executeListRolePrivileges(authorPlan);
     status = permissionInfoResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -261,7 +316,13 @@ public class AuthorInfoTest {
     // list all role of user
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListUserRoles, "user0", "", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListUserRoles,
+            "user0",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     permissionInfoResp = authorInfo.executeListUserRoles(authorPlan);
     status = permissionInfoResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -272,7 +333,13 @@ public class AuthorInfoTest {
     // list all user of role
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListRoleUsers, "", "role0", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListRoleUsers,
+            "",
+            "role0",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     permissionInfoResp = authorInfo.executeListRoleUsers(authorPlan);
     status = permissionInfoResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -290,14 +357,20 @@ public class AuthorInfoTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     status = authorInfo.authorNonQuery(authorPlan);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
     // list root privileges
     authorPlan =
         new AuthorPlan(
-            ConfigPhysicalPlanType.ListUserPrivilege, "root", "", "", "", new 
HashSet<>(), "");
+            ConfigPhysicalPlanType.ListUserPrivilege,
+            "root",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     permissionInfoResp = authorInfo.executeListUserPrivileges(authorPlan);
     status = permissionInfoResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -313,7 +386,8 @@ public class AuthorInfoTest {
 
     // clean user
     AuthorPlan authorPlan =
-        new AuthorPlan(ConfigPhysicalPlanType.ListUser, "", "", "", "", new 
HashSet<>(), "");
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListUser, "", "", "", "", new HashSet<>(), 
new ArrayList<>());
     PermissionInfoResp permissionInfoResp = authorInfo.executeListUser();
     status = permissionInfoResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -322,7 +396,14 @@ public class AuthorInfoTest {
     for (String user : allUsers) {
       if (!user.equals("root")) {
         authorPlan =
-            new AuthorPlan(ConfigPhysicalPlanType.DropUser, user, "", "", "", 
new HashSet<>(), "");
+            new AuthorPlan(
+                ConfigPhysicalPlanType.DropUser,
+                user,
+                "",
+                "",
+                "",
+                new HashSet<>(),
+                new ArrayList<>());
         status = authorInfo.authorNonQuery(authorPlan);
         Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
       }
@@ -336,7 +417,14 @@ public class AuthorInfoTest {
     List<String> roleList = 
permissionInfoResp.getPermissionInfo().get(IoTDBConstant.COLUMN_ROLE);
     for (String roleN : roleList) {
       authorPlan =
-          new AuthorPlan(ConfigPhysicalPlanType.DropRole, "", roleN, "", "", 
new HashSet<>(), "");
+          new AuthorPlan(
+              ConfigPhysicalPlanType.DropRole,
+              "",
+              roleN,
+              "",
+              "",
+              new HashSet<>(),
+              new ArrayList<>());
       status = authorInfo.authorNonQuery(authorPlan);
       Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
     }
@@ -364,4 +452,179 @@ public class AuthorInfoTest {
     Assert.assertEquals(1, 
authorInfo.executeListRole().getPermissionInfo().get("role").size());
     Assert.assertEquals(2, 
authorInfo.executeListUser().getPermissionInfo().get("user").size());
   }
+
+  @Test
+  public void testMultPathsPermission() throws TException, AuthException {
+    TSStatus status;
+
+    AuthorPlan authorPlan;
+
+    Set<Integer> privilegeList = new HashSet<>();
+    privilegeList.add(PrivilegeType.INSERT_TIMESERIES.ordinal());
+    privilegeList.add(PrivilegeType.READ_TIMESERIES.ordinal());
+
+    Map<String, List<String>> permissionInfo;
+    List<String> userPrivilege = new ArrayList<>();
+    userPrivilege.add("root.sg.** : INSERT_TIMESERIES READ_TIMESERIES");
+    userPrivilege.add("root.ln.** : INSERT_TIMESERIES READ_TIMESERIES");
+    Collections.sort(userPrivilege);
+
+    List<String> rolePrivilege = new ArrayList<>();
+    rolePrivilege.add("root.abc.** : INSERT_TIMESERIES READ_TIMESERIES");
+    rolePrivilege.add("root.role_1.** : INSERT_TIMESERIES READ_TIMESERIES");
+    Collections.sort(rolePrivilege);
+
+    List<String> allPrivilege = new ArrayList<>();
+    allPrivilege.addAll(userPrivilege);
+    allPrivilege.addAll(rolePrivilege);
+    Collections.sort(allPrivilege);
+
+    List<String> userPaths = new ArrayList<>();
+    userPaths.add("root.ln.**");
+    userPaths.add("root.sg.**");
+
+    List<String> rolePaths = new ArrayList<>();
+    rolePaths.add("root.role_1.**");
+    rolePaths.add("root.abc.**");
+
+    cleanUserAndRole();
+
+    // create user
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.CreateUser,
+            "user0",
+            "",
+            "passwd",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
+    status = authorInfo.authorNonQuery(authorPlan);
+    Assert.assertNull(status.getMessage());
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+
+    // create role
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.CreateRole,
+            "",
+            "role0",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
+    status = authorInfo.authorNonQuery(authorPlan);
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+
+    // grant user
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.GrantUser, "user0", "", "", "", 
privilegeList, userPaths);
+    status = authorInfo.authorNonQuery(authorPlan);
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+
+    // check user privileges
+    status =
+        authorInfo
+            .checkUserPrivileges("user0", userPaths, 
PrivilegeType.INSERT_TIMESERIES.ordinal())
+            .getStatus();
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+
+    // grant role
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.GrantRole, "", "role0", "", "", 
privilegeList, rolePaths);
+    status = authorInfo.authorNonQuery(authorPlan);
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+
+    // grant role to user
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.GrantRoleToUser,
+            "user0",
+            "role0",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
+    status = authorInfo.authorNonQuery(authorPlan);
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+
+    // list privileges user
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListUserPrivilege,
+            "user0",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            userPaths);
+    PermissionInfoResp permissionInfoResp;
+    permissionInfoResp = authorInfo.executeListUserPrivileges(authorPlan);
+    status = permissionInfoResp.getStatus();
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+    Assert.assertEquals(
+        userPrivilege,
+        
permissionInfoResp.getPermissionInfo().get(IoTDBConstant.COLUMN_PRIVILEGE).stream()
+            .sorted()
+            .collect(Collectors.toList()));
+
+    // list all user privileges
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListUserPrivilege,
+            "user0",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
+    permissionInfoResp = authorInfo.executeListUserPrivileges(authorPlan);
+    status = permissionInfoResp.getStatus();
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+    Assert.assertEquals(
+        allPrivilege,
+        
permissionInfoResp.getPermissionInfo().get(IoTDBConstant.COLUMN_PRIVILEGE).stream()
+            .sorted()
+            .collect(Collectors.toList()));
+
+    // list privileges role
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListRolePrivilege,
+            "",
+            "role0",
+            "",
+            "",
+            new HashSet<>(),
+            rolePaths);
+    permissionInfoResp = authorInfo.executeListRolePrivileges(authorPlan);
+    status = permissionInfoResp.getStatus();
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+    Assert.assertEquals(
+        rolePrivilege,
+        
permissionInfoResp.getPermissionInfo().get(IoTDBConstant.COLUMN_PRIVILEGE).stream()
+            .sorted()
+            .collect(Collectors.toList()));
+
+    // list all role privileges
+    authorPlan =
+        new AuthorPlan(
+            ConfigPhysicalPlanType.ListRolePrivilege,
+            "",
+            "role0",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
+    permissionInfoResp = authorInfo.executeListRolePrivileges(authorPlan);
+    status = permissionInfoResp.getStatus();
+    Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
+    Assert.assertEquals(
+        rolePrivilege,
+        
permissionInfoResp.getPermissionInfo().get(IoTDBConstant.COLUMN_PRIVILEGE).stream()
+            .sorted()
+            .collect(Collectors.toList()));
+  }
 }
diff --git 
a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java
 
b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java
index ee81f23bdd..88b537c218 100644
--- 
a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java
+++ 
b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessorTest.java
@@ -667,7 +667,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "passwd",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
     authorizerReq.setUserName("tempuser1");
@@ -689,14 +689,20 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
     // list user
     authorizerReq =
         new TAuthorizerReq(
-            AuthorOperator.AuthorType.LIST_USER.ordinal(), "", "", "", "", new 
HashSet<>(), "");
+            AuthorOperator.AuthorType.LIST_USER.ordinal(),
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -713,7 +719,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
     authorizerReq.setRoleName("temprole1");
@@ -729,14 +735,20 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
     // list role
     authorizerReq =
         new TAuthorizerReq(
-            AuthorOperator.AuthorType.LIST_ROLE.ordinal(), "", "", "", "", new 
HashSet<>(), "");
+            AuthorOperator.AuthorType.LIST_ROLE.ordinal(),
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -753,11 +765,13 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "newpwd",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
     // grant user
+    List<String> nodeNameList = new ArrayList<>();
+    nodeNameList.add("root.ln.**");
     authorizerReq =
         new TAuthorizerReq(
             AuthorOperator.AuthorType.GRANT_USER.ordinal(),
@@ -766,7 +780,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             privilegeList,
-            "root.ln.**");
+            nodeNameList);
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -785,7 +799,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             privilegeList,
-            "root.ln.**");
+            nodeNameList);
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -798,7 +812,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            nodeNameList);
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -811,7 +825,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             revokePrivilege,
-            "root.ln.**");
+            nodeNameList);
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -824,7 +838,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             revokePrivilege,
-            "root.ln.**");
+            nodeNameList);
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -837,7 +851,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "root.ln.**");
+            nodeNameList);
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -853,7 +867,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -869,7 +883,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "root.ln.**");
+            nodeNameList);
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -886,7 +900,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -902,7 +916,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -919,7 +933,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -937,7 +951,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     status = processor.operatePermission(authorizerReq);
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
 
@@ -950,7 +964,7 @@ public class ConfigNodeRPCServiceProcessorTest {
             "",
             "",
             new HashSet<>(),
-            "");
+            new ArrayList<>());
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -1021,7 +1035,13 @@ public class ConfigNodeRPCServiceProcessorTest {
     // clean user
     TAuthorizerReq authorizerReq =
         new TAuthorizerReq(
-            AuthorOperator.AuthorType.LIST_USER.ordinal(), "", "", "", "", new 
HashSet<>(), "");
+            AuthorOperator.AuthorType.LIST_USER.ordinal(),
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     TAuthorizerResp authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -1037,7 +1057,7 @@ public class ConfigNodeRPCServiceProcessorTest {
                 "",
                 "",
                 new HashSet<>(),
-                "");
+                new ArrayList<>());
         status = processor.operatePermission(authorizerReq);
         Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
       }
@@ -1046,7 +1066,13 @@ public class ConfigNodeRPCServiceProcessorTest {
     // clean role
     authorizerReq =
         new TAuthorizerReq(
-            AuthorOperator.AuthorType.LIST_ROLE.ordinal(), "", "", "", "", new 
HashSet<>(), "");
+            AuthorOperator.AuthorType.LIST_ROLE.ordinal(),
+            "",
+            "",
+            "",
+            "",
+            new HashSet<>(),
+            new ArrayList<>());
     authorizerResp = processor.queryPermission(authorizerReq);
     status = authorizerResp.getStatus();
     Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
@@ -1061,7 +1087,7 @@ public class ConfigNodeRPCServiceProcessorTest {
               "",
               "",
               new HashSet<>(),
-              "");
+              new ArrayList<>());
       status = processor.operatePermission(authorizerReq);
       Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), 
status.getCode());
     }
diff --git a/docs/UserGuide/Administration-Management/Administration.md 
b/docs/UserGuide/Administration-Management/Administration.md
index 5bd3e36749..7eae10a4fe 100644
--- a/docs/UserGuide/Administration-Management/Administration.md
+++ b/docs/UserGuide/Administration-Management/Administration.md
@@ -100,7 +100,7 @@ We use `GRANT USER <userName> PRIVILEGES <privileges> ON 
<nodeName>` to grant us
 
 ```
 GRANT USER `ln_write_user` PRIVILEGES INSERT_TIMESERIES on root.ln.**
-GRANT USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc.**
+GRANT USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc1.**, 
root.sgcc2.**
 GRANT USER `ln_write_user` PRIVILEGES CREATE_USER
 ```
 The execution result is as follows:
@@ -108,7 +108,7 @@ The execution result is as follows:
 ```
 IoTDB> GRANT USER `ln_write_user` PRIVILEGES INSERT_TIMESERIES on root.ln.**
 Msg: The statement is executed successfully.
-IoTDB> GRANT USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on 
root.sgcc.**
+IoTDB> GRANT USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on 
root.sgcc1.**, root.sgcc2.**
 Msg: The statement is executed successfully.
 IoTDB> GRANT USER `ln_write_user` PRIVILEGES CREATE_USER
 Msg: The statement is executed successfully.
@@ -126,7 +126,7 @@ After granting user privileges, we could use `REVOKE USER 
<userName> PRIVILEGES
 
 ```
 REVOKE USER `ln_write_user` PRIVILEGES INSERT_TIMESERIES on root.ln.**
-REVOKE USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc.**
+REVOKE USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc1.**, 
root.sgcc2.**
 REVOKE USER `ln_write_user` PRIVILEGES CREATE_USER
 ```
 
@@ -135,7 +135,7 @@ The execution result is as follows:
 ```
 REVOKE USER `ln_write_user` PRIVILEGES INSERT_TIMESERIES on root.ln.**
 Msg: The statement is executed successfully.
-REVOKE USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc.**
+REVOKE USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc1.**, 
root.sgcc2.**
 Msg: The statement is executed successfully.
 REVOKE USER `ln_write_user` PRIVILEGES CREATE_USER
 Msg: The statement is executed successfully.
@@ -182,28 +182,28 @@ Eg: IoTDB > DROP ROLE `admin`;
 * Grant User Privileges
 
 ```
-GRANT USER <userName> PRIVILEGES <privileges> ON <nodeName>;  
-Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES DELETE_TIMESERIES on root.ln.**;
+GRANT USER <userName> PRIVILEGES <privileges> ON <nodeNames>;  
+Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES INSERT_TIMESERIES, 
DELETE_TIMESERIES on root.ln.**, root.sgcc.**;
 ```
 
 - Grant User All Privileges
 
 ```
-GRANT USER <userName> PRIVILEGES ALL ON <nodeName>; 
-Eg: IoTDB > grant user renyuhua privileges all on root.**
+GRANT USER <userName> PRIVILEGES ALL ON <nodeNames>; 
+Eg: IoTDB > grant user renyuhua privileges all on root.sgcc.**, root.**;
 ```
 
 * Grant Role Privileges
 
 ```
-GRANT ROLE <roleName> PRIVILEGES <privileges> ON <nodeName>;  
-Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES DELETE_TIMESERIES ON root.ln.**;
+GRANT ROLE <roleName> PRIVILEGES <privileges> ON <nodeNames>;  
+Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES INSERT_TIMESERIES, 
DELETE_TIMESERIES ON root.sgcc.**, root.ln.**;
 ```
 
 - Grant Role All Privileges
 
 ```
-GRANT ROLE <roleName> PRIVILEGES ALL ON <nodeName>;  
+GRANT ROLE <roleName> PRIVILEGES ALL ON <nodeNames>;  
 Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES ALL ON root.ln.**;
 ```
 
@@ -217,28 +217,28 @@ Eg: IoTDB > GRANT `temprole` TO tempuser;
 * Revoke User Privileges
 
 ```
-REVOKE USER <userName> PRIVILEGES <privileges> ON <nodeName>;   
+REVOKE USER <userName> PRIVILEGES <privileges> ON <nodeNames>;   
 Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES DELETE_TIMESERIES on root.ln.**;
 ```
 
 * Revoke User All Privileges
 
 ```
-REVOKE USER <userName> PRIVILEGES ALL ON <nodeName>; 
+REVOKE USER <userName> PRIVILEGES ALL ON <nodeNames>; 
 Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES ALL on root.ln.**;
 ```
 
 * Revoke Role Privileges
 
 ```
-REVOKE ROLE <roleName> PRIVILEGES <privileges> ON <nodeName>;  
+REVOKE ROLE <roleName> PRIVILEGES <privileges> ON <nodeNames>;  
 Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES DELETE_TIMESERIES ON root.ln.**;
 ```
 
 * Revoke All Role Privileges
 
 ```
-REVOKE ROLE <roleName> PRIVILEGES ALL ON <nodeName>;  
+REVOKE ROLE <roleName> PRIVILEGES ALL ON <nodeNames>;  
 Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES ALL ON root.ln.**;
 ```
 
@@ -266,8 +266,8 @@ Eg: IoTDB > LIST ROLE
 * List Related Privileges of Users(On Specific Path)
 
 ```
-LIST PRIVILEGES USER <username> ON <path>;
-Eg: IoTDB> LIST PRIVILEGES USER `tempuser` ON root.ln.**;
+LIST PRIVILEGES USER <username> ON <paths>;
+Eg: IoTDB> LIST PRIVILEGES USER `tempuser` ON root.ln.**, root.ln.wf01.**;
 +--------+-----------------------------------+
 |    role|                          privilege|
 +--------+-----------------------------------+
@@ -297,8 +297,8 @@ Eg: IoTDB > LIST ROLE PRIVILEGES `actor`;
 * List Related Privileges of Roles(On Specific Path)
 
 ```
-LIST PRIVILEGES ROLE <roleName> ON <path>;    
-Eg: IoTDB> LIST PRIVILEGES ROLE `temprole` ON root.ln.**;
+LIST PRIVILEGES ROLE <roleName> ON <paths>;    
+Eg: IoTDB> LIST PRIVILEGES ROLE `temprole` ON root.ln.**, root.ln.wf01.wt01.**;
 +-----------------------------------+
 |                          privilege|
 +-----------------------------------+
diff --git a/docs/zh/UserGuide/Administration-Management/Administration.md 
b/docs/zh/UserGuide/Administration-Management/Administration.md
index 692971e393..92f9cc5be6 100644
--- a/docs/zh/UserGuide/Administration-Management/Administration.md
+++ b/docs/zh/UserGuide/Administration-Management/Administration.md
@@ -99,7 +99,7 @@ Msg: 602: No permissions for this operation INSERT
 
 ```
 GRANT USER `ln_write_user` PRIVILEGES INSERT_TIMESERIES on root.ln.**
-GRANT USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc.**
+GRANT USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc1.**, 
root.sgcc2.**
 GRANT USER `ln_write_user` PRIVILEGES CREATE_USER
 ```
 执行状态如下所示:
@@ -107,7 +107,7 @@ GRANT USER `ln_write_user` PRIVILEGES CREATE_USER
 ```
 IoTDB> GRANT USER `ln_write_user` PRIVILEGES INSERT_TIMESERIES on root.ln.**
 Msg: The statement is executed successfully.
-IoTDB> GRANT USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on 
root.sgcc.**
+IoTDB> GRANT USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on 
root.sgcc1.**, root.sgcc2.**
 Msg: The statement is executed successfully.
 IoTDB> GRANT USER `ln_write_user` PRIVILEGES CREATE_USER
 Msg: The statement is executed successfully.
@@ -125,7 +125,7 @@ Msg: The statement is executed successfully.
 
 ```
 REVOKE USER `ln_write_user` PRIVILEGES INSERT_TIMESERIES on root.ln.**
-REVOKE USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc.**
+REVOKE USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc1.**, 
root.sgcc2.**
 REVOKE USER `ln_write_user` PRIVILEGES CREATE_USER
 ```
 
@@ -134,7 +134,7 @@ REVOKE USER `ln_write_user` PRIVILEGES CREATE_USER
 ```
 REVOKE USER `ln_write_user` PRIVILEGES INSERT_TIMESERIES on root.ln.**
 Msg: The statement is executed successfully.
-REVOKE USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc.**
+REVOKE USER `sgcc_write_user` PRIVILEGES INSERT_TIMESERIES on root.sgcc1.**, 
root.sgcc2.**
 Msg: The statement is executed successfully.
 REVOKE USER `ln_write_user` PRIVILEGES CREATE_USER
 Msg: The statement is executed successfully.
@@ -181,28 +181,28 @@ Eg: IoTDB > DROP ROLE `admin`;
 * 赋予用户权限
 
 ```
-GRANT USER <userName> PRIVILEGES <privileges> ON <nodeName>;  
-Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES DELETE_TIMESERIES on root.ln.**;
+GRANT USER <userName> PRIVILEGES <privileges> ON <nodeNames>;  
+Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES INSERT_TIMESERIES, 
DELETE_TIMESERIES on root.ln.**, root.sgcc.**;
 ```
 
 - 赋予用户全部的权限
 
 ```
-GRANT USER <userName> PRIVILEGES ALL ON <nodeName>; 
-Eg: IoTDB > grant user renyuhua privileges all on root.**
+GRANT USER <userName> PRIVILEGES ALL ON <nodeNames>; 
+Eg: IoTDB > grant user renyuhua privileges all on root.sgcc.**, root.**;
 ```
 
 * 赋予角色权限
 
 ```
-GRANT ROLE <roleName> PRIVILEGES <privileges> ON <nodeName>;  
-Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES DELETE_TIMESERIES ON root.ln.**;
+GRANT ROLE <roleName> PRIVILEGES <privileges> ON <nodeNames>;  
+Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES INSERT_TIMESERIES, 
DELETE_TIMESERIES ON root.sgcc.**, root.ln.**;
 ```
 
 - 赋予角色全部的权限
 
 ```
-GRANT ROLE <roleName> PRIVILEGES ALL ON <nodeName>;  
+GRANT ROLE <roleName> PRIVILEGES ALL ON <nodeNames>;  
 Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES ALL ON root.ln.**;
 ```
 
@@ -216,28 +216,28 @@ Eg: IoTDB > GRANT `temprole` TO tempuser;
 * 撤销用户权限
 
 ```
-REVOKE USER <userName> PRIVILEGES <privileges> ON <nodeName>;   
+REVOKE USER <userName> PRIVILEGES <privileges> ON <nodeNames>;   
 Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES DELETE_TIMESERIES on root.ln.**;
 ```
 
 - 移除用户所有权限
 
 ```
-REVOKE USER <userName> PRIVILEGES ALL ON <nodeName>; 
+REVOKE USER <userName> PRIVILEGES ALL ON <nodeNames>; 
 Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES ALL on root.ln.**;
 ```
 
 * 撤销角色权限
 
 ```
-REVOKE ROLE <roleName> PRIVILEGES <privileges> ON <nodeName>;  
+REVOKE ROLE <roleName> PRIVILEGES <privileges> ON <nodeNames>;  
 Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES DELETE_TIMESERIES ON root.ln.**;
 ```
 
 - 撤销角色全部的权限
 
 ```
-REVOKE ROLE <roleName> PRIVILEGES ALL ON <nodeName>;  
+REVOKE ROLE <roleName> PRIVILEGES ALL ON <nodeNames>;  
 Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES ALL ON root.ln.**;
 ```
 
@@ -265,8 +265,8 @@ Eg: IoTDB > LIST ROLE
 * 列出用户在具体路径上相关联的权限
 
 ```    
-LIST PRIVILEGES USER <username> ON <path>;
-Eg: IoTDB> LIST PRIVILEGES USER `tempuser` ON root.ln.**;
+LIST PRIVILEGES USER <username> ON <paths>;
+Eg: IoTDB> LIST PRIVILEGES USER `tempuser` ON root.ln.**, root.ln.wf01.**;
 +--------+-----------------------------------+
 |    role|                          privilege|
 +--------+-----------------------------------+
@@ -296,8 +296,8 @@ Eg: IoTDB > LIST ROLE PRIVILEGES `actor`;
 * 列出角色在具体路径上相关联的权限
 
 ```
-LIST PRIVILEGES ROLE <roleName> ON <path>;    
-Eg: IoTDB> LIST PRIVILEGES ROLE `temprole` ON root.ln.**;
+LIST PRIVILEGES ROLE <roleName> ON <paths>;    
+Eg: IoTDB> LIST PRIVILEGES ROLE `temprole` ON root.ln.**, root.ln.wf01.wt01.**;
 +-----------------------------------+
 |                          privilege|
 +-----------------------------------+
diff --git 
a/node-commons/src/main/java/org/apache/iotdb/commons/utils/BasicStructureSerDeUtil.java
 
b/node-commons/src/main/java/org/apache/iotdb/commons/utils/BasicStructureSerDeUtil.java
index 6979e2eeea..49669553ab 100644
--- 
a/node-commons/src/main/java/org/apache/iotdb/commons/utils/BasicStructureSerDeUtil.java
+++ 
b/node-commons/src/main/java/org/apache/iotdb/commons/utils/BasicStructureSerDeUtil.java
@@ -45,6 +45,19 @@ public class BasicStructureSerDeUtil {
     return new String(bytes, 0, strLength);
   }
 
+  /** read string list from byteBuffer. */
+  public static List<String> readStringList(ByteBuffer buffer) {
+    int size = readInt(buffer);
+    if (size < 0) {
+      return null;
+    }
+    List<String> stringList = new ArrayList<>();
+    for (int i = 0; i < size; i++) {
+      stringList.add(readString(buffer));
+    }
+    return stringList;
+  }
+
   /** read a int var from byteBuffer. */
   public static int readInt(ByteBuffer buffer) {
     return buffer.getInt();
@@ -84,6 +97,24 @@ public class BasicStructureSerDeUtil {
     return len;
   }
 
+  /**
+   * write string list to dataOutputStream.
+   *
+   * @return the length of string represented by byte[].
+   */
+  public static int write(List<String> stringList, DataOutputStream stream) 
throws IOException {
+    if (stringList == null) {
+      throw new IllegalArgumentException("stringList must not be null!");
+    }
+    int res = 0;
+    int size = stringList.size();
+    res += write(size, stream);
+    for (String s : stringList) {
+      res += write(s, stream);
+    }
+    return res;
+  }
+
   /**
    * write a int n to byteBuffer.
    *
diff --git 
a/server/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java 
b/server/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java
index 750b5f9236..9a1bc33de0 100644
--- a/server/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java
+++ b/server/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.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.client.IClientManager;
 import org.apache.iotdb.commons.consensus.PartitionRegionId;
+import org.apache.iotdb.commons.path.PartialPath;
 import org.apache.iotdb.commons.utils.AuthUtils;
 import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq;
 import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp;
@@ -49,10 +50,12 @@ import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 public class ClusterAuthorityFetcher implements IAuthorityFetcher {
   private static final Logger logger = 
LoggerFactory.getLogger(ClusterAuthorityFetcher.class);
@@ -300,6 +303,10 @@ public class ClusterAuthorityFetcher implements 
IAuthorityFetcher {
         authorStatement.getPassWord() == null ? "" : 
authorStatement.getPassWord(),
         authorStatement.getNewPassword() == null ? "" : 
authorStatement.getNewPassword(),
         AuthUtils.strToPermissions(authorStatement.getPrivilegeList()),
-        authorStatement.getNodeName() == null ? "" : 
authorStatement.getNodeName().getFullPath());
+        authorStatement.getNodeNameList() == null
+            ? Collections.emptyList()
+            : authorStatement.getNodeNameList().stream()
+                .map(PartialPath::getFullPath)
+                .collect(Collectors.toList()));
   }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/localconfignode/LocalConfigNode.java 
b/server/src/main/java/org/apache/iotdb/db/localconfignode/LocalConfigNode.java
index a13035438e..7fca93226b 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/localconfignode/LocalConfigNode.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/localconfignode/LocalConfigNode.java
@@ -104,6 +104,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import static 
org.apache.iotdb.commons.conf.IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD;
 
@@ -1059,8 +1060,10 @@ public class LocalConfigNode {
     String password = authorStatement.getPassWord();
     String newPassword = authorStatement.getNewPassword();
     Set<Integer> permissions = 
AuthUtils.strToPermissions(authorStatement.getPrivilegeList());
-    PartialPath partialPath = authorStatement.getNodeName();
-    String nodeName = partialPath == null ? null : partialPath.getFullPath();
+    List<String> nodeNameList =
+        authorStatement.getNodeNameList().stream()
+            .map(PartialPath::getFullPath)
+            .collect(Collectors.toList());
     switch (authorType) {
       case UPDATE_USER:
         iAuthorizer.updateUserPassword(userName, newPassword);
@@ -1079,12 +1082,16 @@ public class LocalConfigNode {
         break;
       case GRANT_ROLE:
         for (int i : permissions) {
-          iAuthorizer.grantPrivilegeToRole(roleName, nodeName, i);
+          for (String path : nodeNameList) {
+            iAuthorizer.grantPrivilegeToRole(roleName, path, i);
+          }
         }
         break;
       case GRANT_USER:
         for (int i : permissions) {
-          iAuthorizer.grantPrivilegeToUser(userName, nodeName, i);
+          for (String path : nodeNameList) {
+            iAuthorizer.grantPrivilegeToUser(userName, path, i);
+          }
         }
         break;
       case GRANT_ROLE_TO_USER:
@@ -1092,12 +1099,16 @@ public class LocalConfigNode {
         break;
       case REVOKE_USER:
         for (int i : permissions) {
-          iAuthorizer.revokePrivilegeFromUser(userName, nodeName, i);
+          for (String path : nodeNameList) {
+            iAuthorizer.revokePrivilegeFromUser(userName, path, i);
+          }
         }
         break;
       case REVOKE_ROLE:
         for (int i : permissions) {
-          iAuthorizer.revokePrivilegeFromRole(roleName, nodeName, i);
+          for (String path : nodeNameList) {
+            iAuthorizer.revokePrivilegeFromRole(roleName, path, i);
+          }
         }
         break;
       case REVOKE_ROLE_FROM_USER:
@@ -1201,16 +1212,20 @@ public class LocalConfigNode {
     } catch (AuthException e) {
       throw new AuthException(e);
     }
-    List<String> rolePrivilegesList = new ArrayList<>();
+    Set<String> rolePrivilegeSet = new HashSet<>();
     for (PathPrivilege pathPrivilege : role.getPrivilegeList()) {
-      if (authorStatement.getNodeName().getFullPath().equals("")
-          || AuthUtils.pathOrBelongsTo(
-              authorStatement.getNodeName().getFullPath(), 
pathPrivilege.getPath())) {
-        rolePrivilegesList.add(pathPrivilege.toString());
+      if (authorStatement.getNodeNameList().isEmpty()) {
+        rolePrivilegeSet.add(pathPrivilege.toString());
+        continue;
+      }
+      for (PartialPath path : authorStatement.getNodeNameList()) {
+        if (AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())) {
+          rolePrivilegeSet.add(pathPrivilege.toString());
+        }
       }
     }
 
-    permissionInfo.put(IoTDBConstant.COLUMN_PRIVILEGE, rolePrivilegesList);
+    permissionInfo.put(IoTDBConstant.COLUMN_PRIVILEGE, new 
ArrayList<>(rolePrivilegeSet));
     return permissionInfo;
   }
 
@@ -1234,27 +1249,45 @@ public class LocalConfigNode {
       }
     } else {
       List<String> rolePrivileges = new ArrayList<>();
+      Set<String> userPrivilegeSet = new HashSet<>();
       for (PathPrivilege pathPrivilege : user.getPrivilegeList()) {
-        if (authorStatement.getNodeName().getFullPath().equals("")
-            || AuthUtils.pathOrBelongsTo(
-                authorStatement.getNodeName().getFullPath(), 
pathPrivilege.getPath())) {
+        if (authorStatement.getNodeNameList().isEmpty()
+            && !userPrivilegeSet.contains(pathPrivilege.toString())) {
           rolePrivileges.add("");
-          userPrivilegesList.add(pathPrivilege.toString());
+          userPrivilegeSet.add(pathPrivilege.toString());
+          continue;
+        }
+        for (PartialPath path : authorStatement.getNodeNameList()) {
+          if (AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())
+              && !userPrivilegeSet.contains(pathPrivilege.toString())) {
+            rolePrivileges.add("");
+            userPrivilegeSet.add(pathPrivilege.toString());
+          }
         }
       }
+      userPrivilegesList.addAll(userPrivilegeSet);
       for (String roleN : user.getRoleList()) {
         Role role = iAuthorizer.getRole(roleN);
         if (roleN == null) {
           continue;
         }
+        Set<String> rolePrivilegeSet = new HashSet<>();
         for (PathPrivilege pathPrivilege : role.getPrivilegeList()) {
-          if (authorStatement.getNodeName().getFullPath().equals("")
-              || AuthUtils.pathOrBelongsTo(
-                  authorStatement.getNodeName().getFullPath(), 
pathPrivilege.getPath())) {
+          if (authorStatement.getNodeNameList().isEmpty()
+              && !rolePrivilegeSet.contains(pathPrivilege.toString())) {
             rolePrivileges.add(roleN);
-            userPrivilegesList.add(pathPrivilege.toString());
+            rolePrivilegeSet.add(pathPrivilege.toString());
+            continue;
+          }
+          for (PartialPath path : authorStatement.getNodeNameList()) {
+            if (AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())
+                && !rolePrivilegeSet.contains(pathPrivilege.toString())) {
+              rolePrivileges.add(roleN);
+              rolePrivilegeSet.add(pathPrivilege.toString());
+            }
           }
         }
+        userPrivilegesList.addAll(rolePrivilegeSet);
       }
       permissionInfo.put(IoTDBConstant.COLUMN_ROLE, rolePrivileges);
     }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
index e8a550df78..2f01be859a 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
@@ -149,6 +149,7 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import java.time.ZoneId;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -156,6 +157,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /** Parse AST to Statement. */
 public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
@@ -1600,17 +1602,20 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
     authorStatement.setPrivilegeList(parsePrivilege(ctx.privileges()));
 
     String privilege = parsePrivilege(ctx.privileges())[0];
-    PartialPath prefixPath;
+    List<PartialPath> nodeNameList;
     if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
       String[] path = {"root"};
-      prefixPath = new PartialPath(path);
+      nodeNameList = Collections.singletonList(new PartialPath(path));
     } else {
       if (ctx.prefixPath() == null) {
         throw new SQLParserException("Invalid prefix path");
       }
-      prefixPath = parsePrefixPath(ctx.prefixPath());
+      nodeNameList =
+          ctx.prefixPath().stream()
+              .map(prefixPath -> parsePrefixPath(prefixPath))
+              .collect(Collectors.toList());
     }
-    authorStatement.setNodeNameList(prefixPath);
+    authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
@@ -1621,7 +1626,11 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorOperator.AuthorType.GRANT_ROLE);
     authorStatement.setRoleName(parseIdentifier(ctx.roleName.getText()));
     authorStatement.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    authorStatement.setNodeNameList(parsePrefixPath(ctx.prefixPath()));
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .collect(Collectors.toList());
+    authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
@@ -1645,17 +1654,20 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
     authorStatement.setPrivilegeList(parsePrivilege(ctx.privileges()));
     String privilege = parsePrivilege(ctx.privileges())[0];
 
-    PartialPath prefixPath;
+    List<PartialPath> nodeNameList;
     if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
       String[] path = {"root"};
-      prefixPath = new PartialPath(path);
+      nodeNameList = Collections.singletonList(new PartialPath(path));
     } else {
       if (ctx.prefixPath() == null) {
         throw new SQLParserException("Invalid prefix path");
       }
-      prefixPath = parsePrefixPath(ctx.prefixPath());
+      nodeNameList =
+          ctx.prefixPath().stream()
+              .map(prefixPath -> parsePrefixPath(prefixPath))
+              .collect(Collectors.toList());
     }
-    authorStatement.setNodeNameList(prefixPath);
+    authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
@@ -1666,7 +1678,11 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorOperator.AuthorType.REVOKE_ROLE);
     authorStatement.setRoleName(parseIdentifier(ctx.roleName.getText()));
     authorStatement.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    authorStatement.setNodeNameList(parsePrefixPath(ctx.prefixPath()));
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .collect(Collectors.toList());
+    authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
@@ -1720,7 +1736,11 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
     AuthorStatement authorStatement =
         new AuthorStatement(AuthorOperator.AuthorType.LIST_USER_PRIVILEGE);
     authorStatement.setUserName(parseIdentifier(ctx.userName.getText()));
-    authorStatement.setNodeNameList(parsePrefixPath(ctx.prefixPath()));
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .collect(Collectors.toList());
+    authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
@@ -1731,7 +1751,11 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
     AuthorStatement authorStatement =
         new AuthorStatement(AuthorOperator.AuthorType.LIST_ROLE_PRIVILEGE);
     authorStatement.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    authorStatement.setNodeNameList(parsePrefixPath(ctx.prefixPath()));
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .collect(Collectors.toList());
+    authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
diff --git 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/sys/AuthorStatement.java
 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/sys/AuthorStatement.java
index 6b136def0b..8ea486b1ec 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/sys/AuthorStatement.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/sys/AuthorStatement.java
@@ -37,7 +37,7 @@ public class AuthorStatement extends Statement implements 
IConfigStatement {
   private String password;
   private String newPassword;
   private String[] privilegeList;
-  private PartialPath nodeName;
+  private List<PartialPath> nodeNameList;
 
   /**
    * AuthorOperator Constructor with AuthorType.
@@ -158,12 +158,12 @@ public class AuthorStatement extends Statement implements 
IConfigStatement {
     this.privilegeList = privilegeList;
   }
 
-  public PartialPath getNodeName() {
-    return nodeName;
+  public List<PartialPath> getNodeNameList() {
+    return nodeNameList != null ? nodeNameList : Collections.emptyList();
   }
 
-  public void setNodeNameList(PartialPath nodePath) {
-    this.nodeName = nodePath;
+  public void setNodeNameList(List<PartialPath> nodeNameList) {
+    this.nodeNameList = nodeNameList;
   }
 
   @Override
@@ -204,6 +204,6 @@ public class AuthorStatement extends Statement implements 
IConfigStatement {
 
   @Override
   public List<PartialPath> getPaths() {
-    return nodeName != null ? Collections.singletonList(nodeName) : 
Collections.emptyList();
+    return nodeNameList != null ? nodeNameList : Collections.emptyList();
   }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java 
b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
index 95f6381c6c..2d3711b20f 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
@@ -1986,7 +1986,7 @@ public class PlanExecutor implements IPlanExecutor {
     String password = author.getPassword();
     String newPassword = author.getNewPassword();
     Set<Integer> permissions = author.getPermissions();
-    PartialPath nodeName = author.getNodeName();
+    List<PartialPath> nodeNameList = author.getNodeNameList();
     try {
       switch (authorType) {
         case UPDATE_USER:
@@ -2006,12 +2006,16 @@ public class PlanExecutor implements IPlanExecutor {
           break;
         case GRANT_ROLE:
           for (int i : permissions) {
-            authorizerManager.grantPrivilegeToRole(roleName, 
nodeName.getFullPath(), i);
+            for (PartialPath path : nodeNameList) {
+              authorizerManager.grantPrivilegeToRole(roleName, 
path.getFullPath(), i);
+            }
           }
           break;
         case GRANT_USER:
           for (int i : permissions) {
-            authorizerManager.grantPrivilegeToUser(userName, 
nodeName.getFullPath(), i);
+            for (PartialPath path : nodeNameList) {
+              authorizerManager.grantPrivilegeToUser(userName, 
path.getFullPath(), i);
+            }
           }
           break;
         case GRANT_ROLE_TO_USER:
@@ -2019,12 +2023,16 @@ public class PlanExecutor implements IPlanExecutor {
           break;
         case REVOKE_USER:
           for (int i : permissions) {
-            authorizerManager.revokePrivilegeFromUser(userName, 
nodeName.getFullPath(), i);
+            for (PartialPath path : nodeNameList) {
+              authorizerManager.revokePrivilegeFromUser(userName, 
path.getFullPath(), i);
+            }
           }
           break;
         case REVOKE_ROLE:
           for (int i : permissions) {
-            authorizerManager.revokePrivilegeFromRole(roleName, 
nodeName.getFullPath(), i);
+            for (PartialPath path : nodeNameList) {
+              authorizerManager.revokePrivilegeFromRole(roleName, 
path.getFullPath(), i);
+            }
           }
           break;
         case REVOKE_ROLE_FROM_USER:
@@ -2224,7 +2232,7 @@ public class PlanExecutor implements IPlanExecutor {
     AuthorType authorType = plan.getAuthorType();
     String userName = plan.getUserName();
     String roleName = plan.getRoleName();
-    PartialPath path = plan.getNodeName();
+    List<PartialPath> nodeNameList = plan.getNodeNameList();
 
     ListDataSet dataSet;
 
@@ -2243,10 +2251,10 @@ public class PlanExecutor implements IPlanExecutor {
           dataSet = executeListUserRoles(userName);
           break;
         case LIST_ROLE_PRIVILEGE:
-          dataSet = executeListRolePrivileges(roleName, path);
+          dataSet = executeListRolePrivileges(roleName, nodeNameList);
           break;
         case LIST_USER_PRIVILEGE:
-          dataSet = executeListUserPrivileges(userName, path);
+          dataSet = executeListUserPrivileges(userName, nodeNameList);
           break;
         default:
           throw new QueryProcessException("Unsupported operation " + 
authorType);
@@ -2300,7 +2308,7 @@ public class PlanExecutor implements IPlanExecutor {
     boolean hasListUserPrivilege =
         AuthorityChecker.check(
             plan.getLoginUserName(),
-            Collections.singletonList((plan.getNodeName())),
+            plan.getNodeNameList(),
             plan.getOperatorType(),
             plan.getLoginUserName());
     if (!hasListUserPrivilege) {
@@ -2357,7 +2365,7 @@ public class PlanExecutor implements IPlanExecutor {
     }
   }
 
-  private ListDataSet executeListRolePrivileges(String roleName, PartialPath 
path)
+  private ListDataSet executeListRolePrivileges(String roleName, 
List<PartialPath> nodeNameList)
       throws AuthException {
     Role role = authorizerManager.getRole(roleName);
     if (role != null) {
@@ -2368,13 +2376,22 @@ public class PlanExecutor implements IPlanExecutor {
       ListDataSet dataSet = new ListDataSet(headerList, typeList);
       int index = 0;
       for (PathPrivilege pathPrivilege : role.getPrivilegeList()) {
-        if (path == null
-            || AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())) {
+        if (nodeNameList.isEmpty()) {
           RowRecord record = new RowRecord(index++);
           Field field = new Field(TSDataType.TEXT);
           field.setBinaryV(new Binary(pathPrivilege.toString()));
           record.addField(field);
           dataSet.putRecord(record);
+          continue;
+        }
+        for (PartialPath path : nodeNameList) {
+          if (AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())) {
+            RowRecord record = new RowRecord(index++);
+            Field field = new Field(TSDataType.TEXT);
+            field.setBinaryV(new Binary(pathPrivilege.toString()));
+            record.addField(field);
+            dataSet.putRecord(record);
+          }
         }
       }
       return dataSet;
@@ -2383,7 +2400,7 @@ public class PlanExecutor implements IPlanExecutor {
     }
   }
 
-  private ListDataSet executeListUserPrivileges(String userName, PartialPath 
path)
+  private ListDataSet executeListUserPrivileges(String userName, 
List<PartialPath> nodeNameList)
       throws AuthException {
     User user = authorizerManager.getUser(userName);
     if (user == null) {
@@ -2411,8 +2428,7 @@ public class PlanExecutor implements IPlanExecutor {
       typeList.add(TSDataType.TEXT);
       ListDataSet dataSet = new ListDataSet(headerList, typeList);
       for (PathPrivilege pathPrivilege : user.getPrivilegeList()) {
-        if (path == null
-            || AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())) {
+        if (nodeNameList.isEmpty()) {
           RowRecord record = new RowRecord(index++);
           Field roleF = new Field(TSDataType.TEXT);
           roleF.setBinaryV(new Binary(""));
@@ -2421,6 +2437,19 @@ public class PlanExecutor implements IPlanExecutor {
           privilegeF.setBinaryV(new Binary(pathPrivilege.toString()));
           record.addField(privilegeF);
           dataSet.putRecord(record);
+          continue;
+        }
+        for (PartialPath path : nodeNameList) {
+          if (AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())) {
+            RowRecord record = new RowRecord(index++);
+            Field roleF = new Field(TSDataType.TEXT);
+            roleF.setBinaryV(new Binary(""));
+            record.addField(roleF);
+            Field privilegeF = new Field(TSDataType.TEXT);
+            privilegeF.setBinaryV(new Binary(pathPrivilege.toString()));
+            record.addField(privilegeF);
+            dataSet.putRecord(record);
+          }
         }
       }
       for (String roleN : user.getRoleList()) {
@@ -2429,8 +2458,7 @@ public class PlanExecutor implements IPlanExecutor {
           continue;
         }
         for (PathPrivilege pathPrivilege : role.getPrivilegeList()) {
-          if (path == null
-              || AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())) {
+          if (nodeNameList.isEmpty()) {
             RowRecord record = new RowRecord(index++);
             Field roleF = new Field(TSDataType.TEXT);
             roleF.setBinaryV(new Binary(roleN));
@@ -2440,6 +2468,18 @@ public class PlanExecutor implements IPlanExecutor {
             record.addField(privilegeF);
             dataSet.putRecord(record);
           }
+          for (PartialPath path : nodeNameList) {
+            if (AuthUtils.pathOrBelongsTo(path.getFullPath(), 
pathPrivilege.getPath())) {
+              RowRecord record = new RowRecord(index++);
+              Field roleF = new Field(TSDataType.TEXT);
+              roleF.setBinaryV(new Binary(roleN));
+              record.addField(roleF);
+              Field privilegeF = new Field(TSDataType.TEXT);
+              privilegeF.setBinaryV(new Binary(pathPrivilege.toString()));
+              record.addField(privilegeF);
+              dataSet.putRecord(record);
+            }
+          }
         }
       }
       return dataSet;
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/AuthorOperator.java 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/AuthorOperator.java
index 263c1e942e..4378872f5f 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/AuthorOperator.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/AuthorOperator.java
@@ -26,6 +26,8 @@ import org.apache.iotdb.db.qp.physical.PhysicalPlan;
 import org.apache.iotdb.db.qp.physical.sys.AuthorPlan;
 import org.apache.iotdb.db.qp.strategy.PhysicalGenerator;
 
+import java.util.List;
+
 /**
  * this class maintains information in Author statement, including CREATE, 
DROP, GRANT and REVOKE.
  */
@@ -37,7 +39,7 @@ public class AuthorOperator extends Operator {
   private String password;
   private String newPassword;
   private String[] privilegeList;
-  private PartialPath nodeName;
+  private List<PartialPath> nodeNameList;
 
   /**
    * AuthorOperator Constructor with AuthorType.
@@ -107,12 +109,12 @@ public class AuthorOperator extends Operator {
     this.privilegeList = authorizationList;
   }
 
-  public PartialPath getNodeName() {
-    return nodeName;
+  public List<PartialPath> getNodeNameList() {
+    return nodeNameList;
   }
 
-  public void setNodeNameList(PartialPath nodePath) {
-    this.nodeName = nodePath;
+  public void setNodeNameList(List<PartialPath> nodePath) {
+    this.nodeNameList = nodePath;
   }
 
   @Override
@@ -120,7 +122,7 @@ public class AuthorOperator extends Operator {
       throws QueryProcessException {
     try {
       return new AuthorPlan(
-          authorType, userName, roleName, password, newPassword, 
privilegeList, nodeName);
+          authorType, userName, roleName, password, newPassword, 
privilegeList, nodeNameList);
     } catch (AuthException e) {
       throw new QueryProcessException(e.getMessage());
     }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/AuthorPlan.java 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/AuthorPlan.java
index cb39680963..06c1a6d9ec 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/AuthorPlan.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/AuthorPlan.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.db.qp.logical.Operator.OperatorType;
 import org.apache.iotdb.db.qp.logical.sys.AuthorOperator;
 import org.apache.iotdb.db.qp.logical.sys.AuthorOperator.AuthorType;
 import org.apache.iotdb.db.qp.physical.PhysicalPlan;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
 
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -36,6 +37,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 public class AuthorPlan extends PhysicalPlan {
 
@@ -44,7 +46,7 @@ public class AuthorPlan extends PhysicalPlan {
   private String password;
   private String newPassword;
   private Set<Integer> permissions;
-  private PartialPath nodeName;
+  private List<PartialPath> nodeNameList;
   private String userName;
 
   /**
@@ -56,7 +58,7 @@ public class AuthorPlan extends PhysicalPlan {
    * @param password password
    * @param newPassword new password
    * @param authorizationList authorization list in String[] structure
-   * @param nodeName node name in Path structure
+   * @param nodeNameList node name in Path structure
    * @throws AuthException Authentication Exception
    */
   public AuthorPlan(
@@ -66,7 +68,7 @@ public class AuthorPlan extends PhysicalPlan {
       String password,
       String newPassword,
       String[] authorizationList,
-      PartialPath nodeName)
+      List<PartialPath> nodeNameList)
       throws AuthException {
     super(Operator.OperatorType.AUTHOR);
     this.authorType = authorType;
@@ -75,7 +77,7 @@ public class AuthorPlan extends PhysicalPlan {
     this.password = password;
     this.newPassword = newPassword;
     this.permissions = AuthUtils.strToPermissions(authorizationList);
-    this.nodeName = nodeName;
+    this.nodeNameList = nodeNameList;
     switch (authorType) {
       case DROP_ROLE:
         this.setOperatorType(Operator.OperatorType.DELETE_ROLE);
@@ -214,8 +216,8 @@ public class AuthorPlan extends PhysicalPlan {
     this.permissions = permissions;
   }
 
-  public PartialPath getNodeName() {
-    return nodeName;
+  public List<PartialPath> getNodeNameList() {
+    return nodeNameList != null ? nodeNameList : Collections.emptyList();
   }
 
   public String getUserName() {
@@ -235,14 +237,14 @@ public class AuthorPlan extends PhysicalPlan {
         + "\npermissions: "
         + permissions
         + "\nnodeName: "
-        + nodeName
+        + nodeNameList
         + "\nauthorType: "
         + authorType;
   }
 
   @Override
   public List<PartialPath> getPaths() {
-    return nodeName != null ? Collections.singletonList(nodeName) : 
Collections.emptyList();
+    return nodeNameList != null ? nodeNameList : Collections.emptyList();
   }
 
   @Override
@@ -260,7 +262,7 @@ public class AuthorPlan extends PhysicalPlan {
         && Objects.equals(getPassword(), that.getPassword())
         && Objects.equals(getNewPassword(), that.getNewPassword())
         && Objects.equals(getPermissions(), that.getPermissions())
-        && Objects.equals(getNodeName(), that.getNodeName());
+        && Objects.equals(getNodeNameList(), that.getNodeNameList());
   }
 
   @Override
@@ -272,7 +274,7 @@ public class AuthorPlan extends PhysicalPlan {
         getPassword(),
         getNewPassword(),
         getPermissions(),
-        getNodeName());
+        getNodeNameList());
   }
 
   @Override
@@ -293,10 +295,11 @@ public class AuthorPlan extends PhysicalPlan {
         stream.writeInt(permission);
       }
     }
-    if (nodeName == null) {
-      putString(stream, null);
+    if (nodeNameList == null) {
+      ReadWriteIOUtils.writeStringList(Collections.emptyList(), stream);
     } else {
-      putString(stream, nodeName.getFullPath());
+      ReadWriteIOUtils.writeStringList(
+          
nodeNameList.stream().map(PartialPath::getFullPath).collect(Collectors.toList()),
 stream);
     }
 
     stream.writeLong(index);
@@ -320,10 +323,11 @@ public class AuthorPlan extends PhysicalPlan {
         buffer.putInt(permission);
       }
     }
-    if (nodeName == null) {
-      putString(buffer, null);
+    if (nodeNameList == null) {
+      ReadWriteIOUtils.writeStringList(Collections.emptyList(), buffer);
     } else {
-      putString(buffer, nodeName.getFullPath());
+      ReadWriteIOUtils.writeStringList(
+          
nodeNameList.stream().map(PartialPath::getFullPath).collect(Collectors.toList()),
 buffer);
     }
 
     buffer.putLong(index);
@@ -346,11 +350,21 @@ public class AuthorPlan extends PhysicalPlan {
         permissions.add(buffer.getInt());
       }
     }
-    String nodeNameStr = readString(buffer);
-    if (nodeNameStr == null) {
-      this.nodeName = null;
+    List<String> nodeNameList = ReadWriteIOUtils.readStringList(buffer);
+    if (nodeNameList.size() == 0) {
+      this.nodeNameList = null;
     } else {
-      this.nodeName = new PartialPath(nodeNameStr);
+      this.nodeNameList =
+          nodeNameList.stream()
+              .map(
+                  path -> {
+                    try {
+                      return new PartialPath(path);
+                    } catch (IllegalPathException e) {
+                      throw new IllegalArgumentException("Deserialize node 
paths failed.");
+                    }
+                  })
+              .collect(Collectors.toList());
     }
 
     this.index = buffer.getLong();
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java 
b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
index 0164e3decf..b0a7aad469 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
@@ -175,6 +175,7 @@ import java.io.File;
 import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -184,6 +185,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import static org.apache.iotdb.db.qp.constant.SQLConstant.TIME_PATH;
 import static org.apache.iotdb.db.qp.constant.SQLConstant.TOK_KILL_QUERY;
@@ -1974,15 +1976,16 @@ public class IoTDBSqlVisitor extends 
IoTDBSqlParserBaseVisitor<Operator> {
     authorOperator.setPrivilegeList(parsePrivilege(ctx.privileges()));
     String privilege = parsePrivilege(ctx.privileges())[0];
 
-    PartialPath prefixPath;
+    List<PartialPath> prefixPath;
     if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
       String[] path = {"root"};
-      prefixPath = new PartialPath(path);
+      prefixPath = Collections.singletonList(new PartialPath(path));
     } else {
       if (ctx.prefixPath() == null) {
         throw new SQLParserException("Invalid prefix path");
       }
-      prefixPath = parsePrefixPath(ctx.prefixPath());
+      prefixPath =
+          ctx.prefixPath().stream().map(path -> 
parsePrefixPath(path)).collect(Collectors.toList());
     }
     authorOperator.setNodeNameList(prefixPath);
     return authorOperator;
@@ -1996,7 +1999,11 @@ public class IoTDBSqlVisitor extends 
IoTDBSqlParserBaseVisitor<Operator> {
         new AuthorOperator(SQLConstant.TOK_AUTHOR_GRANT, 
AuthorType.GRANT_ROLE);
     authorOperator.setRoleName(parseIdentifier(ctx.roleName.getText()));
     authorOperator.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    authorOperator.setNodeNameList(parsePrefixPath(ctx.prefixPath()));
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .collect(Collectors.toList());
+    authorOperator.setNodeNameList(nodeNameList);
     return authorOperator;
   }
 
@@ -2022,17 +2029,20 @@ public class IoTDBSqlVisitor extends 
IoTDBSqlParserBaseVisitor<Operator> {
     authorOperator.setPrivilegeList(parsePrivilege(ctx.privileges()));
     String privilege = parsePrivilege(ctx.privileges())[0];
 
-    PartialPath prefixPath;
+    List<PartialPath> nodeNameList;
     if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
       String[] path = {"root"};
-      prefixPath = new PartialPath(path);
+      nodeNameList = Collections.singletonList(new PartialPath(path));
     } else {
       if (ctx.prefixPath() == null) {
         throw new SQLParserException("Invalid prefix path");
       }
-      prefixPath = parsePrefixPath(ctx.prefixPath());
+      nodeNameList =
+          ctx.prefixPath().stream()
+              .map(prefixPath -> parsePrefixPath(prefixPath))
+              .collect(Collectors.toList());
     }
-    authorOperator.setNodeNameList(prefixPath);
+    authorOperator.setNodeNameList(nodeNameList);
     return authorOperator;
   }
 
@@ -2044,7 +2054,11 @@ public class IoTDBSqlVisitor extends 
IoTDBSqlParserBaseVisitor<Operator> {
         new AuthorOperator(SQLConstant.TOK_AUTHOR_GRANT, 
AuthorType.REVOKE_ROLE);
     authorOperator.setRoleName(parseIdentifier(ctx.roleName.getText()));
     authorOperator.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    authorOperator.setNodeNameList(parsePrefixPath(ctx.prefixPath()));
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .collect(Collectors.toList());
+    authorOperator.setNodeNameList(nodeNameList);
     return authorOperator;
   }
 
@@ -2100,7 +2114,11 @@ public class IoTDBSqlVisitor extends 
IoTDBSqlParserBaseVisitor<Operator> {
     AuthorOperator operator =
         new AuthorOperator(SQLConstant.TOK_LIST, 
AuthorOperator.AuthorType.LIST_USER_PRIVILEGE);
     operator.setUserName(parseIdentifier(ctx.userName.getText()));
-    operator.setNodeNameList(parsePrefixPath(ctx.prefixPath()));
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .collect(Collectors.toList());
+    operator.setNodeNameList(nodeNameList);
     return operator;
   }
 
@@ -2111,7 +2129,11 @@ public class IoTDBSqlVisitor extends 
IoTDBSqlParserBaseVisitor<Operator> {
     AuthorOperator operator =
         new AuthorOperator(SQLConstant.TOK_LIST, 
AuthorOperator.AuthorType.LIST_ROLE_PRIVILEGE);
     operator.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    operator.setNodeNameList(parsePrefixPath(ctx.prefixPath()));
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .collect(Collectors.toList());
+    operator.setNodeNameList(nodeNameList);
     return operator;
   }
 
diff --git 
a/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanTest.java 
b/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanTest.java
index bc25d7b75b..9fce97b490 100644
--- a/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanTest.java
@@ -199,7 +199,7 @@ public class PhysicalPlanTest {
             + "password: null\n"
             + "newPassword: null\n"
             + "permissions: [0, 5]\n"
-            + "nodeName: root.vehicle.d1.s1\n"
+            + "nodeName: [root.vehicle.d1.s1]\n"
             + "authorType: GRANT_ROLE",
         plan.toString());
   }
diff --git a/thrift-confignode/src/main/thrift/confignode.thrift 
b/thrift-confignode/src/main/thrift/confignode.thrift
index bb842ac90e..c8fb44fd2e 100644
--- a/thrift-confignode/src/main/thrift/confignode.thrift
+++ b/thrift-confignode/src/main/thrift/confignode.thrift
@@ -174,7 +174,7 @@ struct TAuthorizerReq {
   4: required string password
   5: required string newPassword
   6: required set<i32> permissions
-  7: required string nodeName
+  7: required list<string> nodeNameList
 }
 
 struct TAuthorizerResp {

Reply via email to