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

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


The following commit(s) were added to refs/heads/auth by this push:
     new c0c3ccea547 fix auth parser. (#11027)
c0c3ccea547 is described below

commit c0c3ccea54755835df48595155df0ecd75257bbe
Author: Colin Li <[email protected]>
AuthorDate: Mon Sep 4 13:59:34 2023 +0800

    fix auth parser. (#11027)
---
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   |  12 +-
 .../db/queryengine/plan/parser/ASTVisitor.java     |  67 +++--
 .../plan/parser/StatementGeneratorTest.java        | 318 +++++++++++++++++++++
 .../org/apache/iotdb/commons/utils/AuthUtils.java  |  14 +-
 .../apache/iotdb/commons/utils/AuthUtilsTest.java  |  38 +--
 5 files changed, 383 insertions(+), 66 deletions(-)

diff --git 
a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 
b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
index 381c1b48fbd..3d7118ff3f2 100644
--- 
a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
+++ 
b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
@@ -861,7 +861,7 @@ grantOpt
 
 // Grant User Role
 grantRoleToUser
-    : GRANT roleName=identifier TO userName=identifier
+    : GRANT ROLE roleName=identifier TO userName=identifier
     ;
 
 // Revoke User Privileges
@@ -876,7 +876,7 @@ revokeRole
 
 // Revoke Role From User
 revokeRoleFromUser
-    : REVOKE roleName=identifier FROM userName=identifier
+    : REVOKE ROLE roleName=identifier FROM userName=identifier
     ;
 
 // Drop User
@@ -899,14 +899,14 @@ listRole
     : LIST ROLE (OF USER userName=usernameWithRoot)?
     ;
 
-// List Privileges of Users On Specific Path
+// List Privileges of Users
 listPrivilegesUser
-    : LIST PRIVILEGES USER userName=usernameWithRoot (ON prefixPath (COMMA 
prefixPath)*)?
+    : LIST PRIVILEGES OF USER userName=usernameWithRoot
     ;
 
-// List Privileges of Roles On Specific Path
+// List Privileges of Roles
 listPrivilegesRole
-    : LIST PRIVILEGES ROLE roleName=identifier (ON prefixPath (COMMA 
prefixPath)*)?
+    : LIST PRIVILEGES OF ROLE roleName=identifier
     ;
 
 privileges
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
index 5909d4f51e4..a96413da6f0 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
@@ -2195,10 +2195,11 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
             .distinct()
             .collect(Collectors.toList());
     checkGrantRevokePrivileges(privileges, nodeNameList);
+    String[] priviParsed = parsePrivilege(privileges);
 
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorType.GRANT_USER);
     authorStatement.setUserName(parseIdentifier(ctx.userName.getText()));
-    authorStatement.setPrivilegeList(privileges);
+    authorStatement.setPrivilegeList(priviParsed);
     authorStatement.setNodeNameList(nodeNameList);
     authorStatement.setGrantOpt(ctx.grantOpt() != null);
     return authorStatement;
@@ -2215,10 +2216,11 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
             .distinct()
             .collect(Collectors.toList());
     checkGrantRevokePrivileges(privileges, nodeNameList);
+    String[] priviParsed = parsePrivilege(privileges);
 
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorType.GRANT_ROLE);
     authorStatement.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    authorStatement.setPrivilegeList(privileges);
+    authorStatement.setPrivilegeList(priviParsed);
     authorStatement.setNodeNameList(nodeNameList);
     authorStatement.setGrantOpt(ctx.grantOpt() != null);
     return authorStatement;
@@ -2245,10 +2247,11 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
             .distinct()
             .collect(Collectors.toList());
     checkGrantRevokePrivileges(privileges, nodeNameList);
+    String[] priviParsed = parsePrivilege(privileges);
 
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorType.REVOKE_USER);
     authorStatement.setUserName(parseIdentifier(ctx.userName.getText()));
-    authorStatement.setPrivilegeList(privileges);
+    authorStatement.setPrivilegeList(priviParsed);
     authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
@@ -2264,40 +2267,66 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
             .distinct()
             .collect(Collectors.toList());
     checkGrantRevokePrivileges(privileges, nodeNameList);
+    String[] priviParsed = parsePrivilege(privileges);
 
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorType.REVOKE_ROLE);
     authorStatement.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    authorStatement.setPrivilegeList(privileges);
+    authorStatement.setPrivilegeList(priviParsed);
     authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
   private void checkGrantRevokePrivileges(String[] privileges, 
List<PartialPath> nodeNameList) {
+    // 1. all grant or revoke statements need target path.
     if (nodeNameList.isEmpty()) {
-      nodeNameList.add(new PartialPath(ALL_RESULT_NODES));
-      return;
+      throw new SemanticException("Statement needs target paths");
     }
-    boolean pathRelevant = true;
+
+    // 2. if privilege list has system privilege or "ALL", nodeNameList must 
only contain "root.**".
+    boolean hasSystemPri = false;
     String errorPrivilegeName = "";
+
     for (String privilege : privileges) {
-      if (!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant()) {
-        pathRelevant = false;
+      if ("ALL".equalsIgnoreCase(privilege)
+          || (!"READ".equalsIgnoreCase(privilege)
+              && !"WRITE".equalsIgnoreCase(privilege)
+              && 
!PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant())) {
+        hasSystemPri = true;
         errorPrivilegeName = privilege.toUpperCase();
         break;
       }
     }
-    if (!(pathRelevant
-        || (nodeNameList.size() == 1
-            && nodeNameList.contains(new PartialPath(ALL_RESULT_NODES))))) {
+    if (hasSystemPri
+        && !(nodeNameList.size() == 1
+            && nodeNameList.contains(new PartialPath(ALL_RESULT_NODES)))) {
       throw new SemanticException(
-          String.format(
-              "path independent privilege: [%s] can only be set on path: 
root.**",
-              errorPrivilegeName));
+          String.format("[%s] can only be set on path: root.**", 
errorPrivilegeName));
     }
   }
 
-  // Revoke Role From User
+  private String[] parsePrivilege(String[] privileges) {
+    Set<String> privSet = new HashSet<>();
+    for (String priv : privileges) {
+      if (priv.equalsIgnoreCase("READ")) {
+        privSet.add("READ_SCHEMA");
+        privSet.add("READ_DATA");
+        continue;
+      } else if (priv.equalsIgnoreCase("WRITE")) {
+        privSet.add("WRITE_DATA");
+        privSet.add("WRITE_SCHEMA");
+        continue;
+      } else if (priv.equalsIgnoreCase("ALL")) {
+        for (PrivilegeType type : PrivilegeType.values()) {
+          privSet.add(type.toString());
+        }
+        continue;
+      }
+      privSet.add(priv);
+    }
+    return privSet.toArray(new String[0]);
+  }
 
+  // Revoke Role From User
   @Override
   public Statement 
visitRevokeRoleFromUser(IoTDBSqlParser.RevokeRoleFromUserContext ctx) {
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorType.REVOKE_USER_ROLE);
@@ -2352,9 +2381,6 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
   public Statement 
visitListPrivilegesUser(IoTDBSqlParser.ListPrivilegesUserContext ctx) {
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorType.LIST_USER_PRIVILEGE);
     authorStatement.setUserName(parseIdentifier(ctx.userName.getText()));
-    List<PartialPath> nodeNameList =
-        
ctx.prefixPath().stream().map(this::parsePrefixPath).collect(Collectors.toList());
-    authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
@@ -2364,9 +2390,6 @@ public class ASTVisitor extends 
IoTDBSqlParserBaseVisitor<Statement> {
   public Statement 
visitListPrivilegesRole(IoTDBSqlParser.ListPrivilegesRoleContext ctx) {
     AuthorStatement authorStatement = new 
AuthorStatement(AuthorType.LIST_ROLE_PRIVILEGE);
     authorStatement.setRoleName(parseIdentifier(ctx.roleName.getText()));
-    List<PartialPath> nodeNameList =
-        
ctx.prefixPath().stream().map(this::parsePrefixPath).collect(Collectors.toList());
-    authorStatement.setNodeNameList(nodeNameList);
     return authorStatement;
   }
 
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java
index ff0ec603365..b8f8645aae6 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java
@@ -20,6 +20,7 @@
 package org.apache.iotdb.db.queryengine.plan.parser;
 
 import org.apache.iotdb.common.rpc.thrift.TAggregationType;
+import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.exception.IllegalPathException;
 import org.apache.iotdb.commons.exception.MetadataException;
 import org.apache.iotdb.commons.path.PartialPath;
@@ -33,6 +34,7 @@ import 
org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
 import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimestampOperand;
 import 
org.apache.iotdb.db.queryengine.plan.expression.multi.FunctionExpression;
 import org.apache.iotdb.db.queryengine.plan.statement.Statement;
+import org.apache.iotdb.db.queryengine.plan.statement.StatementType;
 import org.apache.iotdb.db.queryengine.plan.statement.component.ResultColumn;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.DeleteDataStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement;
@@ -52,6 +54,7 @@ import 
org.apache.iotdb.db.queryengine.plan.statement.metadata.template.CreateSc
 import 
org.apache.iotdb.db.queryengine.plan.statement.metadata.template.DropSchemaTemplateStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.metadata.template.ShowNodesInSchemaTemplateStatement;
 import 
org.apache.iotdb.db.queryengine.plan.statement.metadata.template.UnsetSchemaTemplateStatement;
+import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement;
 import org.apache.iotdb.isession.template.TemplateNode;
 import org.apache.iotdb.mpp.rpc.thrift.TDeleteModelMetricsReq;
 import org.apache.iotdb.mpp.rpc.thrift.TFetchTimeseriesReq;
@@ -89,12 +92,16 @@ import java.nio.ByteBuffer;
 import java.time.ZonedDateTime;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Set;
 
 import static 
org.apache.iotdb.db.schemaengine.template.TemplateQueryType.SHOW_MEASUREMENTS;
 import static 
org.apache.iotdb.tsfile.file.metadata.enums.CompressionType.SNAPPY;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
 
 public class StatementGeneratorTest {
 
@@ -463,6 +470,317 @@ public class StatementGeneratorTest {
     }
   }
 
+  private AuthorStatement createAuthDclStmt(String sql) {
+    Statement stmt = StatementGenerator.createStatement(sql, 
ZonedDateTime.now().getOffset());
+    AuthorStatement roleDcl = (AuthorStatement) stmt;
+    return roleDcl;
+  }
+
+  @Test
+  public void testDCLUserOperation() {
+    // 1. create user and drop user
+    AuthorStatement userDcl = createAuthDclStmt("create user `user1` 
'password1';");
+    assertEquals("user1", userDcl.getUserName());
+    assertEquals(Collections.emptyList(), userDcl.getPaths());
+    assertEquals("password1", userDcl.getPassWord());
+    assertEquals(StatementType.CREATE_USER, userDcl.getType());
+
+    userDcl = createAuthDclStmt("drop user `user1`;");
+    assertEquals("user1", userDcl.getUserName());
+    assertEquals(Collections.emptyList(), userDcl.getPaths());
+    assertEquals(StatementType.DELETE_USER, userDcl.getType());
+
+    // 2.update user's password
+    userDcl = createAuthDclStmt("alter user `user1` set password 
'password2';");
+    assertEquals("user1", userDcl.getUserName());
+    /**
+     * [ BUG ] We didn't save the old password in the statement. If userA has 
logged in with
+     * session_A. Session_B's alter password operation cannot block session 
A's alter password
+     * operation because we only check the user's password before they log in.
+     */
+    // assertEquals("password1", userDcl.getPassWord());
+    assertEquals("password2", userDcl.getNewPassword());
+    assertEquals(StatementType.MODIFY_PASSWORD, userDcl.getType());
+  }
+
+  @Test
+  public void testDCLROLEOperation() {
+    // 1. create role and drop role.
+    AuthorStatement roleDcl = createAuthDclStmt("create role role1;");
+    assertEquals("role1", roleDcl.getRoleName());
+    assertEquals(StatementType.CREATE_ROLE, roleDcl.getType());
+
+    roleDcl = createAuthDclStmt("drop role role1;");
+    assertEquals(StatementType.DELETE_ROLE, roleDcl.getType());
+    assertEquals("role1", roleDcl.getRoleName());
+
+    // 2. grant and revoke role.
+    roleDcl = createAuthDclStmt("grant role `role1` to `user1`;");
+    assertEquals(StatementType.GRANT_USER_ROLE, roleDcl.getType());
+    assertEquals("role1", roleDcl.getRoleName());
+    assertEquals("user1", roleDcl.getUserName());
+
+    roleDcl = createAuthDclStmt("revoke role `role1` from `user1`;");
+    assertEquals(StatementType.GRANT_USER_ROLE, roleDcl.getType());
+    assertEquals("role1", roleDcl.getRoleName());
+    assertEquals("user1", roleDcl.getUserName());
+  }
+
+  @FunctionalInterface
+  interface grantRevokeCheck {
+    void checkParser(String privilege, String name, boolean isuser, String 
path, boolean grantOpt);
+  }
+
+  /** This test will check grant/revoke simple privilege on/from paths. */
+  @Test
+  public void testNormalGrantRevoke() {
+    grantRevokeCheck testGrant =
+        (privilege, name, isuser, path, grantOpt) -> {
+          String sql = "grant %s on %s to %s %s %s ;";
+          sql =
+              String.format(
+                  sql,
+                  privilege,
+                  path,
+                  isuser ? "USER" : "ROLE",
+                  name,
+                  grantOpt ? "with grant option" : "");
+          AuthorStatement aclStmt = createAuthDclStmt(sql);
+          assertEquals(
+              isuser ? StatementType.GRANT_USER_PRIVILEGE : 
StatementType.GRANT_ROLE_PRIVILEGE,
+              aclStmt.getType());
+          assertEquals(path, aclStmt.getPaths().get(0).toString());
+          assertEquals(name, isuser ? aclStmt.getUserName() : 
aclStmt.getRoleName());
+          assertEquals(grantOpt, aclStmt.getGrantOpt());
+          assertEquals(privilege, aclStmt.getPrivilegeList()[0]);
+        };
+
+    String name = "test1";
+    String path = "root.**";
+    String pathErr = "root.t1.**";
+    String pathsErr = "root.**, root.t1.**";
+
+    // 1. check simple privilege grant to user/role with/without grant option.
+    for (PrivilegeType privilege : PrivilegeType.values()) {
+      testGrant.checkParser(privilege.toString(), name, true, path, true);
+      testGrant.checkParser(privilege.toString(), name, true, path, false);
+      testGrant.checkParser(privilege.toString(), name, false, path, true);
+      testGrant.checkParser(privilege.toString(), name, false, path, false);
+      // 2. if grant stmt has system privilege, path should be root.**
+      if (!privilege.isPathRelevant()) {
+        assertThrows(
+            SemanticException.class,
+            () ->
+                createAuthDclStmt(
+                    String.format("GRANT %s on %s to USER `user1`;", 
privilege, pathErr)));
+        assertThrows(
+            SemanticException.class,
+            () ->
+                createAuthDclStmt(
+                    String.format("GRANT %s on %s to USER `user1`;", 
privilege, pathsErr)));
+      }
+    }
+
+    grantRevokeCheck testRevoke =
+        (privilege, username, isuser, targtePath, grantOpt) -> {
+          String sql = "revoke %s on %s from %s %s";
+          sql = String.format(sql, privilege, targtePath, isuser ? "USER" : 
"ROLE", username);
+          AuthorStatement aclStmt = createAuthDclStmt(sql);
+          assertEquals(
+              isuser ? StatementType.REVOKE_USER_PRIVILEGE : 
StatementType.REVOKE_ROLE_PRIVILEGE,
+              aclStmt.getType());
+          assertEquals(path, aclStmt.getPaths().get(0).toString());
+          assertEquals(username, isuser ? aclStmt.getUserName() : 
aclStmt.getRoleName());
+          assertFalse(aclStmt.getGrantOpt());
+          assertEquals(privilege, aclStmt.getPrivilegeList()[0]);
+        };
+
+    // 3. check simple privilege revoke from user/role on simple path
+    for (PrivilegeType type : PrivilegeType.values()) {
+      testRevoke.checkParser(type.toString(), name, true, path, false);
+      testRevoke.checkParser(type.toString(), name, false, path, false);
+
+      // 4. check system privilege revoke from user on wrong paths.
+      if (!type.isPathRelevant()) {
+        assertThrows(
+            SemanticException.class,
+            () ->
+                createAuthDclStmt(
+                    String.format("revoke %s on %s FROM USER `user1`;", type, 
pathErr)));
+        assertThrows(
+            SemanticException.class,
+            () ->
+                createAuthDclStmt(
+                    String.format("revoke %s on %s FROM USER `user1`;", type, 
pathsErr)));
+      }
+    }
+  }
+
+  @Test
+  public void testComplexGrantRevoke() {
+    // 1. test complex privilege on single path :"root.**"
+    Set<String> allPriv = new HashSet<>();
+    for (PrivilegeType type : PrivilegeType.values()) {
+      allPriv.add(type.toString());
+    }
+
+    for (PrivilegeType type : PrivilegeType.values()) {
+      {
+        AuthorStatement stmt =
+            createAuthDclStmt(
+                String.format("GRANT ALL,%s on root.** to USER `user1` with 
grant option", type));
+        assertEquals(allPriv, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+        Assert.assertTrue(stmt.getGrantOpt());
+        assertEquals(StatementType.GRANT_USER_PRIVILEGE, stmt.getType());
+      }
+      {
+        AuthorStatement stmt =
+            createAuthDclStmt(String.format("REVOKE ALL,%s on root.** from 
USER `user1`;", type));
+        assertEquals(allPriv, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+        assertEquals(StatementType.REVOKE_USER_PRIVILEGE, stmt.getType());
+      }
+      {
+        AuthorStatement stmt =
+            createAuthDclStmt(String.format("GRANT ALL,%s on root.** to ROLE 
`role1`;", type));
+        assertEquals(allPriv, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+        assertFalse(stmt.getGrantOpt());
+        assertEquals(StatementType.GRANT_ROLE_PRIVILEGE, stmt.getType());
+      }
+    }
+
+    AuthorStatement stmt =
+        createAuthDclStmt("GRANT ALL ON root.** to user `user1` with grant 
option;");
+    assertEquals(allPriv, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+    Assert.assertTrue(stmt.getGrantOpt());
+    assertEquals(allPriv, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+
+    // 2. complex privilege on a single wrong path
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("grant all on root.t1.** to USER `user1` with 
grant option;"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("grant all on root.t1.** to ROLE `user1` with 
grant option;"));
+    assertThrows(
+        SemanticException.class,
+        () ->
+            createAuthDclStmt(
+                "grant all,READ_DATA on root.t1.** to USER `user1` with grant 
option"));
+    assertThrows(
+        SemanticException.class,
+        () ->
+            createAuthDclStmt(
+                "grant all,READ_DATA on root.t1.** to ROLE `user1` with grant 
option"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("grant all on root.t1.** to USER `user1`;"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("grant all on root.t1.** to ROLE `user1`;"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("grant all,READ_DATA on root.t1.** to USER 
`user1`;"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("grant all,READ_DATA on root.t1.** to ROLE 
`user1`;"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("revoke all on root.t1.** from USER 
`user1`;"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("revoke all on root.t1.** from ROLE 
`user1`;"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("revoke all,READ_DATA on root.t1.** from USER 
`user1`;"));
+    assertThrows(
+        SemanticException.class,
+        () -> createAuthDclStmt("revoke all,READ_DATA on root.t1.** from ROLE 
`user1`;"));
+    try {
+      createAuthDclStmt("grant all on root.t1.**, root.** to USER `user1` with 
grant option;");
+    } catch (SemanticException e) {
+      assertEquals("[ALL] can only be set on path: root.**", e.getMessage());
+    }
+
+    try {
+      createAuthDclStmt("grant MANAGE_ROLE on root.t1.** to USER `user1` with 
grant option;");
+    } catch (SemanticException e) {
+      assertEquals("[MANAGE_ROLE] can only be set on path: root.**", 
e.getMessage());
+    }
+
+    // 3. complex privilege on complex paths.
+    assertThrows(
+        SemanticException.class,
+        () ->
+            createAuthDclStmt(
+                "grant all on root.t1.**, root.** to USER `user1` with grant 
option;"));
+    assertThrows(
+        SemanticException.class,
+        () ->
+            createAuthDclStmt(
+                "grant MANAGE_ROLE on root.t1.**, root.** to USER `user1` with 
grant option;"));
+
+    // 4.  READ privilege can be parsed successfully.
+    stmt = createAuthDclStmt("GRANT READ ON root.** TO USER `user1`;");
+    Set<String> readSet = new HashSet<>();
+    readSet.add("READ_DATA");
+    readSet.add("READ_SCHEMA");
+    assertEquals(readSet, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+
+    stmt = createAuthDclStmt("GRANT READ,READ_DATA ON root.** TO USER 
`user1`;");
+    assertEquals(readSet, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+
+    stmt = createAuthDclStmt("GRANT READ,READ_DATA ON root.**,root.t1.t2 TO 
USER `user1`;");
+    assertEquals(readSet, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+    assertEquals(2, stmt.getPaths().size());
+
+    // 5. WRITE privilege can be parsed successfully.
+    stmt = createAuthDclStmt("GRANT WRITE ON root.** TO USER `user1`;");
+    Set<String> writeSet = new HashSet<>();
+    writeSet.add("WRITE_DATA");
+    writeSet.add("WRITE_SCHEMA");
+    assertEquals(writeSet, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+
+    stmt = createAuthDclStmt("GRANT WRITE,WRITE_DATA ON root.** TO USER 
`user1`;");
+    assertEquals(writeSet, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+
+    stmt = createAuthDclStmt("GRANT WRITE,WRITE ON root.**,root.t1.t2 TO USER 
`user1`;");
+    assertEquals(writeSet, new 
HashSet<>(Arrays.asList(stmt.getPrivilegeList())));
+    assertEquals(2, stmt.getPaths().size());
+  }
+
+  @Test
+  public void testListThings() {
+    // 1. list user
+    AuthorStatement stmt = createAuthDclStmt("LIST USER;");
+    assertEquals(StatementType.LIST_USER, stmt.getType());
+    assertEquals(null, stmt.getRoleName());
+
+    // 2. list user of role
+    stmt = createAuthDclStmt("LIST USER OF ROLE `role1`;");
+    assertEquals(StatementType.LIST_USER, stmt.getType());
+    assertEquals("role1", stmt.getRoleName());
+
+    // 3. list role
+    stmt = createAuthDclStmt("LIST ROLE;");
+    assertEquals(StatementType.LIST_ROLE, stmt.getType());
+    assertEquals(null, stmt.getUserName());
+
+    // 4. list role of user
+    stmt = createAuthDclStmt("LIST ROLE OF USER `user1`;");
+    assertEquals(StatementType.LIST_ROLE, stmt.getType());
+    assertEquals("user1", stmt.getUserName());
+
+    // 5. list privileges of user
+    stmt = createAuthDclStmt("LIST PRIVILEGES OF USER `user1`;");
+    assertEquals(StatementType.LIST_USER_PRIVILEGE, stmt.getType());
+    assertEquals("user1", stmt.getUserName());
+
+    // 6. list privileges of role
+    stmt = createAuthDclStmt("LIST PRIVILEGES OF ROLE `role1`;");
+    assertEquals(StatementType.LIST_ROLE_PRIVILEGE, stmt.getType());
+    assertEquals("role1", stmt.getRoleName());
+  }
+
   // TODO: add more tests
 
   private void checkQueryStatement(
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
index 96266071713..6a576ae5f40 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
@@ -244,9 +244,17 @@ public class AuthUtils {
       return false;
     }
     for (PathPrivilege pathPrivilege : privilegeList) {
-      if (pathPrivilege.getPath().matchFullPath(path)
-          && pathPrivilege.getPrivileges().contains(privilegeId)) {
-        return true;
+      if (pathPrivilege.getPath().matchFullPath(path)) {
+        if (pathPrivilege.getPrivileges().contains(privilegeId)) {
+          return true;
+        }
+        if (privilegeId == PrivilegeType.READ_SCHEMA.ordinal()
+            && 
pathPrivilege.getPrivileges().contains(PrivilegeType.WRITE_SCHEMA.ordinal()))
+          return true;
+        if (privilegeId == PrivilegeType.READ_DATA.ordinal()
+            && 
pathPrivilege.getPrivileges().contains(PrivilegeType.WRITE_DATA.ordinal())) {
+          return true;
+        }
       }
     }
     return false;
diff --git 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java
 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java
index b0048e09bd0..97a88ace8d1 100644
--- 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java
+++ 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java
@@ -30,40 +30,8 @@ import org.junit.Test;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Vector;
 
 public class AuthUtilsTest {
-  @Test
-  public void authUtilsTest_ParameterCheck() throws AuthException, 
IllegalPathException {
-    AuthUtils auth;
-    Vector<String> nameOrPassword = new Vector<>();
-    nameOrPassword.add(new String("he"));
-    nameOrPassword.add(
-        new String(
-            "qwertyuiopasdfghjklzxcvbnm123456789999999asdfgh"
-                + "jkzxcvbnmqwertyuioasdfghjklzxcvbnm"));
-    nameOrPassword.add(new String("he  llo"));
-    nameOrPassword.add(new String("hel^d"));
-    nameOrPassword.add(new String("he\\llo"));
-    nameOrPassword.add(new String("he*llo"));
-    nameOrPassword.add(new String("he*$llo"));
-    for (String item : nameOrPassword) {
-      Assert.assertThrows(AuthException.class, () -> 
AuthUtils.validateNameOrPassword(item));
-    }
-    PartialPath path1 = new PartialPath(new String("data.t1"));
-    PartialPath path2 = new PartialPath(new String("root.t1"));
-    Assert.assertThrows(AuthException.class, () -> 
AuthUtils.validatePath(path1));
-    Assert.assertThrows(AuthException.class, () -> 
AuthUtils.validatePrivilege(-1));
-    // give a wrong path
-    Assert.assertThrows(AuthException.class, () -> 
AuthUtils.validatePrivilege(path1, -1));
-    // give a path but a wrong privilege id
-    Assert.assertThrows(AuthException.class, () -> 
AuthUtils.validatePrivilege(path2, 5));
-
-    Assert.assertThrows(AuthException.class, () -> 
AuthUtils.validatePrivilege(null, 3));
-    AuthUtils.validatePrivilege(path2, PrivilegeType.WRITE_SCHEMA.ordinal());
-    AuthUtils.validatePrivilege(null, PrivilegeType.MANAGE_ROLE.ordinal());
-  }
-
   @Test
   public void authUtilsTest_PrivilegeGrantRevokeCheck() throws 
IllegalPathException {
     PartialPath path = new PartialPath(new String("root.t1"));
@@ -79,7 +47,7 @@ public class AuthUtilsTest {
     PathPrivilege pathWithPri3 = new PathPrivilege(path3);
     pathWithPri3.grantPrivilege(PrivilegeType.READ_DATA.ordinal(), false);
 
-    /** root.t1 : read schema, read data root.t2 : write schema root.** : read 
data */
+    /** root.t1 : read schema, read data; root.t2 : write schema; root.** : 
read data */
     // Privilege list is empty.
     Assert.assertFalse(
         AuthUtils.checkPathPrivilege(path2, 
PrivilegeType.READ_SCHEMA.ordinal(), null));
@@ -88,13 +56,13 @@ public class AuthUtilsTest {
     privilegeList.add(pathWithPri);
     privilegeList.add(pathWithPri2);
     privilegeList.add(pathWithPri3);
-    Assert.assertFalse(
+    Assert.assertTrue(
         AuthUtils.checkPathPrivilege(path2, 
PrivilegeType.READ_SCHEMA.ordinal(), privilegeList));
     Assert.assertTrue(
         AuthUtils.checkPathPrivilege(path, 
PrivilegeType.READ_SCHEMA.ordinal(), privilegeList));
 
     pathWithPri.revokePrivilege(PrivilegeType.READ_SCHEMA.ordinal());
-    /** root.t1 : read data root.t2 : write schema root.** : read data */
+    /** root.t1 : read data; root.t2 : write schema ; root.** : read data */
     Assert.assertFalse(
         AuthUtils.checkPathPrivilege(path, 
PrivilegeType.READ_SCHEMA.ordinal(), privilegeList));
     Assert.assertTrue(

Reply via email to