You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2025/02/07 11:47:15 UTC

(iotdb) branch MaintainAuth created (now f69aa983339)

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

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


      at f69aa983339 Support authentication for maintain statement

This branch includes the following new commits:

     new f69aa983339 Support authentication for maintain statement

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(iotdb) 01/01: Support authentication for maintain statement

Posted by ja...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f69aa9833395c5d52a76d51422a4a7cd7a60d48c
Author: JackieTien97 <ja...@gmail.com>
AuthorDate: Fri Feb 7 19:47:01 2025 +0800

    Support authentication for maintain statement
---
 .../org/apache/iotdb/db/it/utils/TestUtils.java    |  34 ++++
 .../it/query/recent/IoTDBMaintainAuthIT.java       | 215 +++++++++++++++++++++
 .../it/query/recent/IoTDBQueryAuthIT.java          |   3 +-
 .../plan/execution/config/ConfigExecution.java     |   4 +-
 .../execution/config/TableConfigTaskVisitor.java   |   1 +
 .../config/executor/ClusterConfigTaskExecutor.java |  17 +-
 6 files changed, 267 insertions(+), 7 deletions(-)

diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/utils/TestUtils.java b/integration-test/src/test/java/org/apache/iotdb/db/it/utils/TestUtils.java
index 15de78c0e3d..330a2475915 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/utils/TestUtils.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/utils/TestUtils.java
@@ -268,6 +268,40 @@ public class TestUtils {
     }
   }
 
+  public static void tableExecuteTest(String sql, String userName, String password) {
+    try (Connection connection =
+        EnvFactory.getEnv().getConnection(userName, password, BaseEnv.TABLE_SQL_DIALECT)) {
+      connection.setClientInfo("time_zone", "+00:00");
+      try (Statement statement = connection.createStatement()) {
+        statement.execute(sql);
+      }
+    } catch (SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  public static void tableQueryNoVerifyResultTest(
+      String sql, String[] expectedHeader, String userName, String password, String database) {
+    try (Connection connection =
+        EnvFactory.getEnv().getConnection(userName, password, BaseEnv.TABLE_SQL_DIALECT)) {
+      connection.setClientInfo("time_zone", "+00:00");
+      try (Statement statement = connection.createStatement()) {
+        statement.execute("use " + database);
+        try (ResultSet resultSet = statement.executeQuery(sql)) {
+          ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
+          for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
+            assertEquals(expectedHeader[i - 1], resultSetMetaData.getColumnName(i));
+          }
+          assertEquals(expectedHeader.length, resultSetMetaData.getColumnCount());
+        }
+      }
+    } catch (SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
   public static void tableResultSetEqualTest(
       String sql,
       String[] expectedHeader,
diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBMaintainAuthIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBMaintainAuthIT.java
new file mode 100644
index 00000000000..df340a46cd0
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBMaintainAuthIT.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.relational.it.query.recent;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.TableClusterIT;
+import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.prepareTableData;
+import static org.apache.iotdb.db.it.utils.TestUtils.tableAssertTestFail;
+import static org.apache.iotdb.db.it.utils.TestUtils.tableExecuteTest;
+import static org.apache.iotdb.db.it.utils.TestUtils.tableQueryNoVerifyResultTest;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
+public class IoTDBMaintainAuthIT {
+  private static final String DATABASE_NAME = "test";
+  private static final String CREATE_USER_FORMAT = "create user %s '%s'";
+  private static final String USER_1 = "user1";
+  private static final String USER_2 = "user2";
+  private static final String PASSWORD = "password";
+
+  private static final String[] createSqls =
+      new String[] {
+        "CREATE DATABASE " + DATABASE_NAME,
+        "USE " + DATABASE_NAME,
+        "CREATE TABLE table1(device_id STRING TAG, s1 INT32 FIELD)",
+        "INSERT INTO table1(time,device_id,s1) values(1, 'd1', 1)",
+        String.format(CREATE_USER_FORMAT, USER_1, PASSWORD),
+        "GRANT MAINTAIN TO USER " + USER_1,
+        "GRANT SELECT ON TABLE table1 TO USER " + USER_1,
+        "GRANT SELECT ON information_schema.queries TO USER " + USER_1,
+        String.format(CREATE_USER_FORMAT, USER_2, PASSWORD)
+      };
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    EnvFactory.getEnv().initClusterEnvironment();
+    prepareTableData(createSqls);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void maintainAuthTest() {
+    // case 1: explain
+    // user1 with select on table1
+    String[] expectedHeader = new String[] {"distribution plan"};
+    tableQueryNoVerifyResultTest(
+        "explain select * from table1", expectedHeader, USER_1, PASSWORD, DATABASE_NAME);
+    // user2 without select on table1
+    tableAssertTestFail(
+        "explain select * from test.table1",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege SELECT ON test.table1",
+        USER_2,
+        PASSWORD);
+
+    // case 2: explain analyze [verbose]
+    // user1 with select on table1
+    expectedHeader = new String[] {"Explain Analyze"};
+    tableQueryNoVerifyResultTest(
+        "explain analyze select * from table1", expectedHeader, USER_1, PASSWORD, DATABASE_NAME);
+    tableQueryNoVerifyResultTest(
+        "explain analyze verbose select * from table1",
+        expectedHeader,
+        USER_1,
+        PASSWORD,
+        DATABASE_NAME);
+    // user2 without select on table1
+    tableAssertTestFail(
+        "explain analyze select * from test.table1",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege SELECT ON test.table1",
+        USER_2,
+        PASSWORD);
+    tableAssertTestFail(
+        "explain analyze verbose select * from test.table1",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege SELECT ON test.table1",
+        USER_2,
+        PASSWORD);
+
+    // case 3: show current_sql_dialect
+    expectedHeader = new String[] {"CurrentSqlDialect"};
+    tableQueryNoVerifyResultTest(
+        "SHOW CURRENT_SQL_DIALECT", expectedHeader, USER_2, PASSWORD, DATABASE_NAME);
+
+    // case 4: show current_user
+    expectedHeader = new String[] {"CurrentUser"};
+    tableQueryNoVerifyResultTest(
+        "SHOW CURRENT_USER", expectedHeader, USER_2, PASSWORD, DATABASE_NAME);
+
+    // case 5: show version
+    expectedHeader = new String[] {"Version", "BuildInfo"};
+    tableQueryNoVerifyResultTest("SHOW VERSION", expectedHeader, USER_2, PASSWORD, DATABASE_NAME);
+
+    // case 6: show current_timestamp
+    expectedHeader = new String[] {"CurrentTimestamp"};
+    tableQueryNoVerifyResultTest(
+        "SHOW CURRENT_TIMESTAMP", expectedHeader, USER_2, PASSWORD, DATABASE_NAME);
+
+    // case 7: show variables
+    expectedHeader = new String[] {"Variable", "Value"};
+    // user1 with MAINTAIN
+    tableQueryNoVerifyResultTest("SHOW VARIABLES", expectedHeader, USER_1, PASSWORD, DATABASE_NAME);
+    // user2 without MAINTAIN
+    tableAssertTestFail(
+        "SHOW VARIABLES",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege MAINTAIN",
+        USER_2,
+        PASSWORD);
+
+    // case 8: show cluster_id
+    expectedHeader = new String[] {"ClusterId"};
+    // user1 with MAINTAIN
+    tableQueryNoVerifyResultTest(
+        "SHOW CLUSTER_ID", expectedHeader, USER_1, PASSWORD, DATABASE_NAME);
+    // user2 without MAINTAIN
+    tableAssertTestFail(
+        "SHOW CLUSTER_ID",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege MAINTAIN",
+        USER_2,
+        PASSWORD);
+
+    // case 9: flush
+    // user1 with MAINTAIN
+    tableExecuteTest("FLUSH", USER_1, PASSWORD);
+    // user2 without MAINTAIN
+    tableAssertTestFail(
+        "FLUSH",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege MAINTAIN",
+        USER_2,
+        PASSWORD);
+
+    // case 10: clear cache
+    // user1 with MAINTAIN
+    tableExecuteTest("CLEAR CACHE", USER_1, PASSWORD);
+    // user2 without MAINTAIN
+    tableAssertTestFail(
+        "CLEAR CACHE",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege MAINTAIN",
+        USER_2,
+        PASSWORD);
+
+    // case 11: set configuration
+    // user1 with MAINTAIN
+    tableExecuteTest("SET CONFIGURATION query_timeout_threshold='100000'", USER_1, PASSWORD);
+    // user2 without MAINTAIN
+    tableAssertTestFail(
+        "SET CONFIGURATION query_timeout_threshold='100000'",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege MAINTAIN",
+        USER_2,
+        PASSWORD);
+
+    // case 12: show queries
+    // user1 with select on information_schema.queries
+    tableExecuteTest("show queries", USER_1, PASSWORD);
+    // user2 without select on information_schema.queries
+    tableAssertTestFail(
+        "show queries",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege SELECT ON information_schema.queries",
+        USER_2,
+        PASSWORD);
+
+    // case 12: kill query
+    // user1 with MAINTAIN
+    tableAssertTestFail(
+        "kill query '20250206_093300_00001_100'",
+        TSStatusCode.NO_SUCH_QUERY.getStatusCode() + ": No such query",
+        USER_1,
+        PASSWORD);
+    // user2 without MAINTAIN
+    tableAssertTestFail(
+        "kill query '20250206_093300_00001_100'",
+        TSStatusCode.NO_PERMISSION.getStatusCode()
+            + ": Access Denied: No permissions for this operation, please add privilege MAINTAIN",
+        USER_2,
+        PASSWORD);
+  }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBQueryAuthIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBQueryAuthIT.java
index 88ac066261f..4557a66e7d1 100644
--- a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBQueryAuthIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBQueryAuthIT.java
@@ -81,7 +81,6 @@ public class IoTDBQueryAuthIT {
 
   @BeforeClass
   public static void setUp() throws Exception {
-    EnvFactory.getEnv().getConfig().getCommonConfig().setEnableCrossSpaceCompaction(false);
     EnvFactory.getEnv().initClusterEnvironment();
     prepareTableData(createSqls);
   }
@@ -92,7 +91,7 @@ public class IoTDBQueryAuthIT {
   }
 
   @Test
-  public void normalFillTest() {
+  public void queryAuthTest() {
     // case 1: user1 with SELECT ON ANY
     String[] expectedHeader1 = new String[] {"time", "device_id", "s1"};
     String[] retArray1 =
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/ConfigExecution.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/ConfigExecution.java
index 92a8a979d65..17c75ee30a8 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/ConfigExecution.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/ConfigExecution.java
@@ -93,7 +93,9 @@ public class ConfigExecution implements IQueryExecution {
                   TSStatusCode.ROLE_NOT_EXIST.getStatusCode(),
                   TSStatusCode.USER_ALREADY_HAS_ROLE.getStatusCode(),
                   TSStatusCode.USER_NOT_HAS_ROLE.getStatusCode(),
-                  TSStatusCode.NOT_HAS_PRIVILEGE_GRANTOPT.getStatusCode())));
+                  TSStatusCode.NOT_HAS_PRIVILEGE_GRANTOPT.getStatusCode(),
+                  TSStatusCode.SEMANTIC_ERROR.getStatusCode(),
+                  TSStatusCode.NO_SUCH_QUERY.getStatusCode())));
 
   private final MPPQueryContext context;
   private final ExecutorService executor;
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
index 221b0255ea8..be17215ad86 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
@@ -974,6 +974,7 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont
   @Override
   protected IConfigTask visitKillQuery(KillQuery node, MPPQueryContext context) {
     context.setQueryType(QueryType.WRITE);
+    accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName());
     return new KillQueryTask(node);
   }
 
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java
index 2f3ed1a0f2d..ff7e13416a7 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java
@@ -150,7 +150,6 @@ import org.apache.iotdb.db.exception.BatchProcessException;
 import org.apache.iotdb.db.exception.StorageEngineException;
 import org.apache.iotdb.db.exception.metadata.PathNotExistException;
 import org.apache.iotdb.db.exception.metadata.SchemaQuotaExceededException;
-import org.apache.iotdb.db.exception.sql.SemanticException;
 import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent;
 import org.apache.iotdb.db.protocol.client.ConfigNodeClient;
 import org.apache.iotdb.db.protocol.client.ConfigNodeClientManager;
@@ -1344,19 +1343,29 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor {
   public SettableFuture<ConfigTaskResult> killQuery(final KillQueryStatement killQueryStatement) {
     int dataNodeId = -1;
     String queryId = killQueryStatement.getQueryId();
+    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
     if (!killQueryStatement.isKillAll()) {
       String[] splits = queryId.split("_");
       try {
         // We just judge the input queryId has three '_' and the DataNodeId from it is non-negative
         // here
         if (splits.length != 4 || ((dataNodeId = Integer.parseInt(splits[3])) < 0)) {
-          throw new SemanticException("Please ensure your input <queryId> is correct");
+          future.setException(
+              new IoTDBException(
+                  "Please ensure your input <queryId> is correct",
+                  TSStatusCode.SEMANTIC_ERROR.getStatusCode(),
+                  true));
+          return future;
         }
       } catch (NumberFormatException e) {
-        throw new SemanticException("Please ensure your input <queryId> is correct");
+        future.setException(
+            new IoTDBException(
+                "Please ensure your input <queryId> is correct",
+                TSStatusCode.SEMANTIC_ERROR.getStatusCode(),
+                true));
+        return future;
       }
     }
-    SettableFuture<ConfigTaskResult> future = SettableFuture.create();
     try (ConfigNodeClient client =
         CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) {
       final TSStatus executionStatus = client.killQuery(queryId, dataNodeId);