You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by zy...@apache.org on 2022/08/17 12:04:33 UTC

[iotdb] branch master updated: [IOTDB-4153]Grant ALL privileges to different paths return privilege exists (#7023)

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

zyk 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 16a5f7ab5b [IOTDB-4153]Grant ALL privileges to different paths return privilege exists (#7023)
16a5f7ab5b is described below

commit 16a5f7ab5bc0979ee1b8d5347a408813ec6e4e25
Author: Yifu Zhou <ef...@outlook.com>
AuthorDate: Wed Aug 17 20:04:27 2022 +0800

    [IOTDB-4153]Grant ALL privileges to different paths return privilege exists (#7023)
    
    [IOTDB-4153]Grant ALL privileges to different paths return privilege exists (#7023)
---
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   |   4 +-
 .../Administration-Management/Administration.md    |  20 ++--
 .../Administration-Management/Administration.md    |  22 +++--
 .../iotdb/db/integration/IoTDBAuthorizationIT.java |  90 ++++++++++++++++++
 .../iotdb/db/mpp/plan/parser/ASTVisitor.java       |  96 ++++++++++++-------
 .../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java    | 103 +++++++++++++--------
 6 files changed, 243 insertions(+), 92 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 71f210c4c9..15a9239d00 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
@@ -537,7 +537,7 @@ grantUser
 
 // Grant Role Privileges
 grantRole
-    : GRANT ROLE roleName=identifier PRIVILEGES privileges ON prefixPath (COMMA prefixPath)*
+    : GRANT ROLE roleName=identifier PRIVILEGES privileges (ON prefixPath (COMMA prefixPath)*)?
     ;
 
 // Grant User Role
@@ -552,7 +552,7 @@ revokeUser
 
 // Revoke Role Privileges
 revokeRole
-    : REVOKE ROLE roleName=identifier PRIVILEGES privileges ON prefixPath (COMMA prefixPath)*
+    : REVOKE ROLE roleName=identifier PRIVILEGES privileges (ON prefixPath (COMMA prefixPath)*)?
     ;
 
 // Revoke Role From User
diff --git a/docs/UserGuide/Administration-Management/Administration.md b/docs/UserGuide/Administration-Management/Administration.md
index f918cfbd28..42fef4811b 100644
--- a/docs/UserGuide/Administration-Management/Administration.md
+++ b/docs/UserGuide/Administration-Management/Administration.md
@@ -184,13 +184,14 @@ Eg: IoTDB > DROP ROLE `admin`;
 ```
 GRANT USER <userName> PRIVILEGES <privileges> ON <nodeNames>;  
 Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES INSERT_TIMESERIES, DELETE_TIMESERIES on root.ln.**, root.sgcc.**;
+Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES CREATE_ROLE;
 ```
 
 - Grant User All Privileges
 
 ```
-GRANT USER <userName> PRIVILEGES ALL ON <nodeNames>; 
-Eg: IoTDB > grant user renyuhua privileges all on root.sgcc.**, root.**;
+GRANT USER <userName> PRIVILEGES ALL; 
+Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES ALL;
 ```
 
 * Grant Role Privileges
@@ -198,13 +199,14 @@ Eg: IoTDB > grant user renyuhua privileges all on root.sgcc.**, root.**;
 ```
 GRANT ROLE <roleName> PRIVILEGES <privileges> ON <nodeNames>;  
 Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES INSERT_TIMESERIES, DELETE_TIMESERIES ON root.sgcc.**, root.ln.**;
+Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES CREATE_ROLE;
 ```
 
 - Grant Role All Privileges
 
 ```
 GRANT ROLE <roleName> PRIVILEGES ALL ON <nodeNames>;  
-Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES ALL ON root.ln.**;
+Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES ALL;
 ```
 
 * Grant User Role
@@ -219,13 +221,14 @@ Eg: IoTDB > GRANT `temprole` TO tempuser;
 ```
 REVOKE USER <userName> PRIVILEGES <privileges> ON <nodeNames>;   
 Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES DELETE_TIMESERIES on root.ln.**;
+Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES CREATE_ROLE;
 ```
 
 * Revoke User All Privileges
 
 ```
-REVOKE USER <userName> PRIVILEGES ALL ON <nodeNames>; 
-Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES ALL on root.ln.**;
+REVOKE USER <userName> PRIVILEGES ALL; 
+Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES ALL;
 ```
 
 * Revoke Role Privileges
@@ -233,13 +236,14 @@ Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES ALL on root.ln.**;
 ```
 REVOKE ROLE <roleName> PRIVILEGES <privileges> ON <nodeNames>;  
 Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES DELETE_TIMESERIES ON root.ln.**;
+Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES CREATE_ROLE;
 ```
 
 * Revoke All Role Privileges
 
 ```
-REVOKE ROLE <roleName> PRIVILEGES ALL ON <nodeNames>;  
-Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES ALL ON root.ln.**;
+REVOKE ROLE <roleName> PRIVILEGES ALL;  
+Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES ALL;
 ```
 
 * Revoke Role From User
@@ -396,6 +400,8 @@ At the same time, changes to roles are immediately reflected on all users who ow
 |APPLY_TEMPLATE|set, unset and activate schema template; path dependent|Eg1: `set schema template t1 to root.sg.d`<br/>Eg2: `create timeseries of schema template on root.sg.d`
 |READ_TEMPLATE_APPLICATION|show paths set and using schema template; path independent|Eg1: `show paths set schema template t1`<br/>Eg2: `show paths using schema template t1`
 
+Note that path dependent privileges can only be granted or revoked on root.**;
+
 Note that the following SQL statements need to be granted multiple permissions before they can be used:
 
 - Import data: Need to assign `READ_TIMESERIES`,`INSERT_TIMESERIES` two permissions.。
diff --git a/docs/zh/UserGuide/Administration-Management/Administration.md b/docs/zh/UserGuide/Administration-Management/Administration.md
index 700ca0820c..fdb92620d6 100644
--- a/docs/zh/UserGuide/Administration-Management/Administration.md
+++ b/docs/zh/UserGuide/Administration-Management/Administration.md
@@ -183,13 +183,14 @@ Eg: IoTDB > DROP ROLE `admin`;
 ```
 GRANT USER <userName> PRIVILEGES <privileges> ON <nodeNames>;  
 Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES INSERT_TIMESERIES, DELETE_TIMESERIES on root.ln.**, root.sgcc.**;
+Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES CREATE_ROLE;
 ```
 
 - 赋予用户全部的权限
 
 ```
-GRANT USER <userName> PRIVILEGES ALL ON <nodeNames>; 
-Eg: IoTDB > grant user renyuhua privileges all on root.sgcc.**, root.**;
+GRANT USER <userName> PRIVILEGES ALL; 
+Eg: IoTDB > GRANT USER `tempuser` PRIVILEGES ALL;
 ```
 
 * 赋予角色权限
@@ -197,13 +198,14 @@ Eg: IoTDB > grant user renyuhua privileges all on root.sgcc.**, root.**;
 ```
 GRANT ROLE <roleName> PRIVILEGES <privileges> ON <nodeNames>;  
 Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES INSERT_TIMESERIES, DELETE_TIMESERIES ON root.sgcc.**, root.ln.**;
+Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES CREATE_ROLE;
 ```
 
 - 赋予角色全部的权限
 
 ```
-GRANT ROLE <roleName> PRIVILEGES ALL ON <nodeNames>;  
-Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES ALL ON root.ln.**;
+GRANT ROLE <roleName> PRIVILEGES ALL;  
+Eg: IoTDB > GRANT ROLE `temprole` PRIVILEGES ALL;
 ```
 
 * 赋予用户角色
@@ -218,13 +220,14 @@ Eg: IoTDB > GRANT `temprole` TO tempuser;
 ```
 REVOKE USER <userName> PRIVILEGES <privileges> ON <nodeNames>;   
 Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES DELETE_TIMESERIES on root.ln.**;
+Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES CREATE_ROLE;
 ```
 
 - 移除用户所有权限
 
 ```
-REVOKE USER <userName> PRIVILEGES ALL ON <nodeNames>; 
-Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES ALL on root.ln.**;
+REVOKE USER <userName> PRIVILEGES ALL; 
+Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES ALL;
 ```
 
 * 撤销角色权限
@@ -232,13 +235,14 @@ Eg: IoTDB > REVOKE USER `tempuser` PRIVILEGES ALL on root.ln.**;
 ```
 REVOKE ROLE <roleName> PRIVILEGES <privileges> ON <nodeNames>;  
 Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES DELETE_TIMESERIES ON root.ln.**;
+Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES CREATE_ROLE;
 ```
 
 - 撤销角色全部的权限
 
 ```
-REVOKE ROLE <roleName> PRIVILEGES ALL ON <nodeNames>;  
-Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES ALL ON root.ln.**;
+REVOKE ROLE <roleName> PRIVILEGES ALL;  
+Eg: IoTDB > REVOKE ROLE `temprole` PRIVILEGES ALL;
 ```
 
 * 撤销用户角色
@@ -395,6 +399,8 @@ Eg: IoTDB > ALTER USER `tempuser` SET PASSWORD 'newpwd';
 |APPLY_TEMPLATE|挂载、卸载、激活模板。路径有关。|Eg1: `set schema template t1 to root.sg.d`<br/>Eg2: `create timeseries of schema template on root.sg.d`
 |READ_TEMPLATE_APPLICATION|查看模板的挂载路径和激活路径。路径无关|Eg1: `show paths set schema template t1`<br/>Eg2: `show paths using schema template t1`
 
+注意: 路径无关的权限只能在路径root.**下赋予或撤销;
+
 注意: 下述sql语句需要赋予多个权限才可以使用:
 
 - 导入数据,需要赋予`READ_TIMESERIES`,`INSERT_TIMESERIES`两种权限。
diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBAuthorizationIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBAuthorizationIT.java
index 183c7576c3..1e5196fc77 100644
--- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBAuthorizationIT.java
+++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBAuthorizationIT.java
@@ -1398,4 +1398,94 @@ public class IoTDBAuthorizationIT {
       assertFalse(resultSet.next());
     }
   }
+
+  @Test
+  public void testCheckGrantRevokePrivileges() throws ClassNotFoundException, SQLException {
+    Class.forName(Config.JDBC_DRIVER_NAME);
+    try (Connection adminCon =
+            DriverManager.getConnection(
+                Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+        Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("CREATE USER tempuser 'temppw'");
+
+      adminStmt.execute("GRANT USER tempuser PRIVILEGES ALL on root.**");
+      adminStmt.execute("REVOKE USER tempuser PRIVILEGES ALL on root.**");
+      adminStmt.execute("GRANT USER tempuser PRIVILEGES ALL");
+      adminStmt.execute(
+          "GRANT USER tempuser PRIVILEGES INSERT_TIMESERIES, READ_TIMESERIES on root.ln.**");
+      adminStmt.execute(
+          "REVOKE USER tempuser PRIVILEGES INSERT_TIMESERIES, READ_TIMESERIES on root.ln.**");
+      boolean caught = false;
+      try {
+        adminStmt.execute("GRANT USER tempuser PRIVILEGES ALL on root.ln.**");
+      } catch (Exception e) {
+        caught = true;
+      }
+      assertTrue(caught);
+
+      caught = false;
+      try {
+        adminStmt.execute("REVOKE USER tempuser PRIVILEGES ALL on root.ln.**");
+      } catch (Exception e) {
+        caught = true;
+      }
+      assertTrue(caught);
+
+      caught = false;
+      try {
+        adminStmt.execute("GRANT USER tempuser PRIVILEGES INSERT_TIMESERIES, ALL on root.ln.**");
+      } catch (Exception e) {
+        caught = true;
+      }
+      assertTrue(caught);
+
+      caught = false;
+      try {
+        adminStmt.execute("REVOKE USER tempuser PRIVILEGES INSERT_TIMESERIES, ALL on root.ln.**");
+      } catch (Exception e) {
+        caught = true;
+      }
+      assertTrue(caught);
+
+      adminStmt.execute("CREATE ROLE temprole");
+      adminStmt.execute("GRANT ROLE temprole PRIVILEGES ALL on root.**");
+      adminStmt.execute("REVOKE ROLE temprole PRIVILEGES ALL on root.**");
+      adminStmt.execute("GRANT ROLE temprole PRIVILEGES ALL");
+      adminStmt.execute(
+          "GRANT ROLE temprole PRIVILEGES INSERT_TIMESERIES, READ_TIMESERIES on root.ln.**");
+      adminStmt.execute(
+          "REVOKE ROLE temprole PRIVILEGES INSERT_TIMESERIES, READ_TIMESERIES on root.ln.**");
+      caught = false;
+      try {
+        adminStmt.execute("GRANT ROLE temprole PRIVILEGES ALL on root.ln.**");
+      } catch (Exception e) {
+        caught = true;
+      }
+      assertTrue(caught);
+
+      caught = false;
+      try {
+        adminStmt.execute("REVOKE ROLE temprole PRIVILEGES ALL on root.ln.**");
+      } catch (Exception e) {
+        caught = true;
+      }
+      assertTrue(caught);
+
+      caught = false;
+      try {
+        adminStmt.execute("GRANT ROLE temprole PRIVILEGES INSERT_TIMESERIES, ALL on root.ln.**");
+      } catch (Exception e) {
+        caught = true;
+      }
+      assertTrue(caught);
+
+      caught = false;
+      try {
+        adminStmt.execute("REVOKE ROLE temprole PRIVILEGES INSERT_TIMESERIES, ALL on root.ln.**");
+      } catch (Exception e) {
+        caught = true;
+      }
+      assertTrue(caught);
+    }
+  }
 }
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 d306127944..760a403d14 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
@@ -162,6 +162,8 @@ import java.util.Set;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import static org.apache.iotdb.db.metadata.MetadataConstant.ALL_RESULT_NODES;
+
 /** Parse AST to Statement. */
 public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
 
@@ -1622,22 +1624,17 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
 
   @Override
   public Statement visitGrantUser(IoTDBSqlParser.GrantUserContext ctx) {
+    String[] privileges = parsePrivilege(ctx.privileges());
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(this::parsePrefixPath)
+            .distinct()
+            .collect(Collectors.toList());
+    checkGrantRevokePrivileges(privileges, nodeNameList);
+
     AuthorStatement authorStatement = new AuthorStatement(AuthorOperator.AuthorType.GRANT_USER);
     authorStatement.setUserName(parseIdentifier(ctx.userName.getText()));
-    authorStatement.setPrivilegeList(parsePrivilege(ctx.privileges()));
-
-    String privilege = parsePrivilege(ctx.privileges())[0];
-    List<PartialPath> nodeNameList;
-    if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
-      String[] path = {"root"};
-      nodeNameList = Collections.singletonList(new PartialPath(path));
-    } else {
-      if (ctx.prefixPath() == null) {
-        throw new SQLParserException("Invalid prefix path");
-      }
-      nodeNameList =
-          ctx.prefixPath().stream().map(this::parsePrefixPath).collect(Collectors.toList());
-    }
+    authorStatement.setPrivilegeList(privileges);
     authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
@@ -1646,11 +1643,17 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
 
   @Override
   public Statement visitGrantRole(IoTDBSqlParser.GrantRoleContext ctx) {
+    String[] privileges = parsePrivilege(ctx.privileges());
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(this::parsePrefixPath)
+            .distinct()
+            .collect(Collectors.toList());
+    checkGrantRevokePrivileges(privileges, nodeNameList);
+
     AuthorStatement authorStatement = new AuthorStatement(AuthorOperator.AuthorType.GRANT_ROLE);
     authorStatement.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    authorStatement.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    List<PartialPath> nodeNameList =
-        ctx.prefixPath().stream().map(this::parsePrefixPath).collect(Collectors.toList());
+    authorStatement.setPrivilegeList(privileges);
     authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
@@ -1670,22 +1673,17 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
 
   @Override
   public Statement visitRevokeUser(IoTDBSqlParser.RevokeUserContext ctx) {
+    String[] privileges = parsePrivilege(ctx.privileges());
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(this::parsePrefixPath)
+            .distinct()
+            .collect(Collectors.toList());
+    checkGrantRevokePrivileges(privileges, nodeNameList);
+
     AuthorStatement authorStatement = new AuthorStatement(AuthorOperator.AuthorType.REVOKE_USER);
     authorStatement.setUserName(parseIdentifier(ctx.userName.getText()));
-    authorStatement.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    String privilege = parsePrivilege(ctx.privileges())[0];
-
-    List<PartialPath> nodeNameList;
-    if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
-      String[] path = {"root"};
-      nodeNameList = Collections.singletonList(new PartialPath(path));
-    } else {
-      if (ctx.prefixPath() == null) {
-        throw new SQLParserException("Invalid prefix path");
-      }
-      nodeNameList =
-          ctx.prefixPath().stream().map(this::parsePrefixPath).collect(Collectors.toList());
-    }
+    authorStatement.setPrivilegeList(privileges);
     authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
@@ -1694,15 +1692,45 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
 
   @Override
   public Statement visitRevokeRole(IoTDBSqlParser.RevokeRoleContext ctx) {
+    String[] privileges = parsePrivilege(ctx.privileges());
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(this::parsePrefixPath)
+            .distinct()
+            .collect(Collectors.toList());
+    checkGrantRevokePrivileges(privileges, nodeNameList);
+
     AuthorStatement authorStatement = new AuthorStatement(AuthorOperator.AuthorType.REVOKE_ROLE);
     authorStatement.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    authorStatement.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    List<PartialPath> nodeNameList =
-        ctx.prefixPath().stream().map(this::parsePrefixPath).collect(Collectors.toList());
+    authorStatement.setPrivilegeList(privileges);
     authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
+  private void checkGrantRevokePrivileges(String[] privileges, List<PartialPath> nodeNameList) {
+    if (nodeNameList.isEmpty()) {
+      nodeNameList.addAll(Collections.singletonList(new PartialPath(ALL_RESULT_NODES)));
+      return;
+    }
+    boolean pathRelevant = true;
+    String errorPrivilegeName = "";
+    for (String privilege : privileges) {
+      if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
+        pathRelevant = false;
+        errorPrivilegeName = privilege.toUpperCase();
+        break;
+      }
+    }
+    if (!(pathRelevant
+        || (nodeNameList.size() == 1
+            && nodeNameList.contains(new PartialPath(ALL_RESULT_NODES))))) {
+      throw new SQLParserException(
+          String.format(
+              "path independent privilege: [%s] can only be set on path: root.**",
+              errorPrivilegeName));
+    }
+  }
+
   // Revoke Role From User
 
   @Override
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 fd3ce0b205..7be4b3058c 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
@@ -184,6 +184,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import static org.apache.iotdb.db.metadata.MetadataConstant.ALL_RESULT_NODES;
 import static org.apache.iotdb.db.qp.constant.SQLConstant.TIME_PATH;
 import static org.apache.iotdb.db.qp.constant.SQLConstant.TOK_KILL_QUERY;
 
@@ -1967,24 +1968,19 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
 
   @Override
   public Operator visitGrantUser(IoTDBSqlParser.GrantUserContext ctx) {
+    String[] privileges = parsePrivilege(ctx.privileges());
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(this::parsePrefixPath)
+            .distinct()
+            .collect(Collectors.toList());
+    checkGrantRevokePrivileges(privileges, nodeNameList);
+
     AuthorOperator authorOperator =
         new AuthorOperator(SQLConstant.TOK_AUTHOR_GRANT, AuthorOperator.AuthorType.GRANT_USER);
     authorOperator.setUserName(parseIdentifier(ctx.userName.getText()));
-    authorOperator.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    String privilege = parsePrivilege(ctx.privileges())[0];
-
-    List<PartialPath> prefixPath;
-    if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
-      String[] path = {"root"};
-      prefixPath = Collections.singletonList(new PartialPath(path));
-    } else {
-      if (ctx.prefixPath() == null) {
-        throw new SQLParserException("Invalid prefix path");
-      }
-      prefixPath =
-          ctx.prefixPath().stream().map(path -> parsePrefixPath(path)).collect(Collectors.toList());
-    }
-    authorOperator.setNodeNameList(prefixPath);
+    authorOperator.setPrivilegeList(privileges);
+    authorOperator.setNodeNameList(nodeNameList);
     return authorOperator;
   }
 
@@ -1992,14 +1988,18 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
 
   @Override
   public Operator visitGrantRole(IoTDBSqlParser.GrantRoleContext ctx) {
-    AuthorOperator authorOperator =
-        new AuthorOperator(SQLConstant.TOK_AUTHOR_GRANT, AuthorType.GRANT_ROLE);
-    authorOperator.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    authorOperator.setPrivilegeList(parsePrivilege(ctx.privileges()));
+    String[] privileges = parsePrivilege(ctx.privileges());
     List<PartialPath> nodeNameList =
         ctx.prefixPath().stream()
-            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .map(this::parsePrefixPath)
+            .distinct()
             .collect(Collectors.toList());
+    checkGrantRevokePrivileges(privileges, nodeNameList);
+
+    AuthorOperator authorOperator =
+        new AuthorOperator(SQLConstant.TOK_AUTHOR_GRANT, AuthorType.GRANT_ROLE);
+    authorOperator.setRoleName(parseIdentifier(ctx.roleName.getText()));
+    authorOperator.setPrivilegeList(privileges);
     authorOperator.setNodeNameList(nodeNameList);
     return authorOperator;
   }
@@ -2020,25 +2020,18 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
 
   @Override
   public Operator visitRevokeUser(IoTDBSqlParser.RevokeUserContext ctx) {
+    String[] privileges = parsePrivilege(ctx.privileges());
+    List<PartialPath> nodeNameList =
+        ctx.prefixPath().stream()
+            .map(this::parsePrefixPath)
+            .distinct()
+            .collect(Collectors.toList());
+    checkGrantRevokePrivileges(privileges, nodeNameList);
+
     AuthorOperator authorOperator =
         new AuthorOperator(SQLConstant.TOK_AUTHOR_GRANT, AuthorType.REVOKE_USER);
     authorOperator.setUserName(parseIdentifier(ctx.userName.getText()));
-    authorOperator.setPrivilegeList(parsePrivilege(ctx.privileges()));
-    String privilege = parsePrivilege(ctx.privileges())[0];
-
-    List<PartialPath> nodeNameList;
-    if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
-      String[] path = {"root"};
-      nodeNameList = Collections.singletonList(new PartialPath(path));
-    } else {
-      if (ctx.prefixPath() == null) {
-        throw new SQLParserException("Invalid prefix path");
-      }
-      nodeNameList =
-          ctx.prefixPath().stream()
-              .map(prefixPath -> parsePrefixPath(prefixPath))
-              .collect(Collectors.toList());
-    }
+    authorOperator.setPrivilegeList(privileges);
     authorOperator.setNodeNameList(nodeNameList);
     return authorOperator;
   }
@@ -2047,18 +2040,46 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
 
   @Override
   public Operator visitRevokeRole(IoTDBSqlParser.RevokeRoleContext ctx) {
-    AuthorOperator authorOperator =
-        new AuthorOperator(SQLConstant.TOK_AUTHOR_GRANT, AuthorType.REVOKE_ROLE);
-    authorOperator.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    authorOperator.setPrivilegeList(parsePrivilege(ctx.privileges()));
+    String[] privileges = parsePrivilege(ctx.privileges());
     List<PartialPath> nodeNameList =
         ctx.prefixPath().stream()
-            .map(prefixPath -> parsePrefixPath(prefixPath))
+            .map(this::parsePrefixPath)
+            .distinct()
             .collect(Collectors.toList());
+    checkGrantRevokePrivileges(privileges, nodeNameList);
+
+    AuthorOperator authorOperator =
+        new AuthorOperator(SQLConstant.TOK_AUTHOR_GRANT, AuthorType.REVOKE_ROLE);
+    authorOperator.setRoleName(parseIdentifier(ctx.roleName.getText()));
+    authorOperator.setPrivilegeList(privileges);
     authorOperator.setNodeNameList(nodeNameList);
     return authorOperator;
   }
 
+  private void checkGrantRevokePrivileges(String[] privileges, List<PartialPath> nodeNameList) {
+    if (nodeNameList.isEmpty()) {
+      nodeNameList.addAll(Collections.singletonList(new PartialPath(ALL_RESULT_NODES)));
+      return;
+    }
+    boolean pathRelevant = true;
+    String errorPrivilegeName = "";
+    for (String privilege : privileges) {
+      if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
+        pathRelevant = false;
+        errorPrivilegeName = privilege.toUpperCase();
+        break;
+      }
+    }
+    if (!(pathRelevant
+        || (nodeNameList.size() == 1
+            && nodeNameList.contains(new PartialPath(ALL_RESULT_NODES))))) {
+      throw new SQLParserException(
+          String.format(
+              "path independent privilege: [%s] can only be set on path: root.**",
+              errorPrivilegeName));
+    }
+  }
+
   // Revoke Role From User
 
   @Override