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

jiangtian 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 77ff8f91730 Fix that oldPassword is not recorded during user creation 
& delete password history after user removal (#16416)
77ff8f91730 is described below

commit 77ff8f91730500815011e2d2a7a1d684bbe42ddd
Author: Jiang Tian <[email protected]>
AuthorDate: Mon Sep 15 17:43:28 2025 +0800

    Fix that oldPassword is not recorded during user creation & delete password 
history after user removal (#16416)
---
 .../org/apache/iotdb/db/it/auth/IoTDBAuthIT.java   | 28 ++++++++++-
 .../iotdb/db/protocol/session/SessionManager.java  |  2 +-
 .../sql/ast/RelationalAuthorStatement.java         | 24 ++++++++--
 .../plan/statement/sys/AuthorStatement.java        | 24 ++++++++--
 .../apache/iotdb/db/utils/DataNodeAuthUtils.java   | 54 +++++++++++++++++++++-
 5 files changed, 121 insertions(+), 11 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
index 98467604409..0d963f9e1a4 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
@@ -1550,7 +1550,7 @@ public class IoTDBAuthIT {
   public void testPasswordHistory() {
     try (Connection connection = EnvFactory.getEnv().getConnection();
         Statement statement = connection.createStatement()) {
-      testPasswordHistoryCreate(statement);
+      testPasswordHistoryCreateAndDrop(statement);
       testPasswordHistoryAlter(statement);
     } catch (SQLException e) {
       e.printStackTrace();
@@ -1558,7 +1558,7 @@ public class IoTDBAuthIT {
     }
   }
 
-  public void testPasswordHistoryCreate(Statement statement) throws 
SQLException {
+  public void testPasswordHistoryCreateAndDrop(Statement statement) throws 
SQLException {
     statement.execute("create user userA 'abcdef123456'");
 
     try (ResultSet resultSet =
@@ -1569,9 +1569,33 @@ public class IoTDBAuthIT {
       }
       assertEquals(AuthUtils.encryptPassword("abcdef123456"), 
resultSet.getString("Value"));
     }
+
+    try (ResultSet resultSet =
+        statement.executeQuery(
+            "select last oldPassword from 
root.__system.password_history.`_userA`")) {
+      if (!resultSet.next()) {
+        fail("Password history not found");
+      }
+      assertEquals(AuthUtils.encryptPassword("abcdef123456"), 
resultSet.getString("Value"));
+    }
+
+    statement.execute("drop user userA");
+
+    try (ResultSet resultSet =
+        statement.executeQuery(
+            "select last password from 
root.__system.password_history.`_userA`")) {
+      assertFalse(resultSet.next());
+    }
+
+    try (ResultSet resultSet =
+        statement.executeQuery(
+            "select last oldPassword from 
root.__system.password_history.`_userA`")) {
+      assertFalse(resultSet.next());
+    }
   }
 
   public void testPasswordHistoryAlter(Statement statement) throws 
SQLException {
+    statement.execute("create user userA 'abcdef123456'");
     statement.execute("alter user userA set password 'abcdef654321'");
 
     try (ResultSet resultSet =
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java
index 9fa85a0be76..a5a04e98b27 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java
@@ -265,7 +265,7 @@ public class SessionManager implements SessionManagerMBean {
               username);
           long currentTime = CommonDateTimeUtils.currentTime();
           TSStatus tsStatus =
-              DataNodeAuthUtils.recordPassword(username, password, null, 
currentTime);
+              DataNodeAuthUtils.recordPasswordHistory(username, password, 
password, currentTime);
           if (tsStatus.getCode() != 
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
             openSessionResp
                 .sessionId(-1)
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java
index 4e2b4597672..a66d6f022c0 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java
@@ -20,6 +20,7 @@ package 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast;
 
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
+import org.apache.iotdb.commons.utils.AuthUtils;
 import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
 import org.apache.iotdb.db.queryengine.plan.analyze.QueryType;
 import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType;
@@ -261,14 +262,21 @@ public class RelationalAuthorStatement extends Statement {
       return onCreateUserSuccess();
     } else if (authorType == AuthorRType.UPDATE_USER) {
       return onUpdateUserSuccess();
+    } else if (authorType == AuthorRType.DROP_USER) {
+      return onDropUserSuccess();
     }
     return null;
   }
 
   private TSStatus onCreateUserSuccess() {
+    // the old password is expected to be encrypted during updates, so we also 
encrypt it here to
+    // keep consistency
     TSStatus tsStatus =
-        DataNodeAuthUtils.recordPassword(
-            userName, password, null, CommonDateTimeUtils.currentTime());
+        DataNodeAuthUtils.recordPasswordHistory(
+            userName,
+            password,
+            AuthUtils.encryptPassword(password),
+            CommonDateTimeUtils.currentTime());
     try {
       RpcUtils.verifySuccess(tsStatus);
     } catch (StatementExecutionException e) {
@@ -279,7 +287,7 @@ public class RelationalAuthorStatement extends Statement {
 
   private TSStatus onUpdateUserSuccess() {
     TSStatus tsStatus =
-        DataNodeAuthUtils.recordPassword(
+        DataNodeAuthUtils.recordPasswordHistory(
             userName, password, oldPassword, 
CommonDateTimeUtils.currentTime());
     try {
       RpcUtils.verifySuccess(tsStatus);
@@ -289,6 +297,16 @@ public class RelationalAuthorStatement extends Statement {
     return null;
   }
 
+  private TSStatus onDropUserSuccess() {
+    TSStatus tsStatus = DataNodeAuthUtils.deletePasswordHistory(userName);
+    try {
+      RpcUtils.verifySuccess(tsStatus);
+    } catch (StatementExecutionException e) {
+      return new TSStatus(e.getStatusCode()).setMessage(e.getMessage());
+    }
+    return null;
+  }
+
   public String getOldPassword() {
     return oldPassword;
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/AuthorStatement.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/AuthorStatement.java
index 1cc2b3d499c..e34f289b742 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/AuthorStatement.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/AuthorStatement.java
@@ -22,6 +22,7 @@ package org.apache.iotdb.db.queryengine.plan.statement.sys;
 import org.apache.iotdb.common.rpc.thrift.TSStatus;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
 import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.commons.utils.AuthUtils;
 import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
 import org.apache.iotdb.db.auth.AuthorityChecker;
 import org.apache.iotdb.db.queryengine.plan.analyze.QueryType;
@@ -360,14 +361,21 @@ public class AuthorStatement extends Statement implements 
IConfigStatement {
       return onCreateUserSuccess();
     } else if (authorType == AuthorType.UPDATE_USER) {
       return onUpdateUserSuccess();
+    } else if (authorType == AuthorType.DROP_USER) {
+      return onDropUserSuccess();
     }
     return null;
   }
 
   private TSStatus onCreateUserSuccess() {
+    // the old password is expected to be encrypted during updates, so we also 
encrypt it here to
+    // keep consistency
     TSStatus tsStatus =
-        DataNodeAuthUtils.recordPassword(
-            userName, password, null, CommonDateTimeUtils.currentTime());
+        DataNodeAuthUtils.recordPasswordHistory(
+            userName,
+            password,
+            AuthUtils.encryptPassword(password),
+            CommonDateTimeUtils.currentTime());
     try {
       RpcUtils.verifySuccess(tsStatus);
     } catch (StatementExecutionException e) {
@@ -378,7 +386,7 @@ public class AuthorStatement extends Statement implements 
IConfigStatement {
 
   private TSStatus onUpdateUserSuccess() {
     TSStatus tsStatus =
-        DataNodeAuthUtils.recordPassword(
+        DataNodeAuthUtils.recordPasswordHistory(
             userName, newPassword, password, 
CommonDateTimeUtils.currentTime());
     try {
       RpcUtils.verifySuccess(tsStatus);
@@ -387,4 +395,14 @@ public class AuthorStatement extends Statement implements 
IConfigStatement {
     }
     return null;
   }
+
+  private TSStatus onDropUserSuccess() {
+    TSStatus tsStatus = DataNodeAuthUtils.deletePasswordHistory(userName);
+    try {
+      RpcUtils.verifySuccess(tsStatus);
+    } catch (StatementExecutionException e) {
+      return new TSStatus(e.getStatusCode()).setMessage(e.getMessage());
+    }
+    return null;
+  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DataNodeAuthUtils.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DataNodeAuthUtils.java
index 180fb3e3e68..dcc95f366c8 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DataNodeAuthUtils.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/DataNodeAuthUtils.java
@@ -40,6 +40,7 @@ import 
org.apache.iotdb.db.queryengine.plan.execution.IQueryExecution;
 import org.apache.iotdb.db.queryengine.plan.parser.StatementGenerator;
 import org.apache.iotdb.db.queryengine.plan.statement.Statement;
 import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
+import 
org.apache.iotdb.db.queryengine.plan.statement.metadata.DeleteTimeSeriesStatement;
 import org.apache.iotdb.rpc.TSStatusCode;
 
 import org.apache.tsfile.enums.TSDataType;
@@ -50,6 +51,7 @@ import org.slf4j.LoggerFactory;
 
 import java.nio.charset.StandardCharsets;
 import java.time.ZoneId;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.Optional;
 
@@ -144,13 +146,13 @@ public class DataNodeAuthUtils {
         currentTimeMillis);
   }
 
-  public static TSStatus recordPassword(
+  public static TSStatus recordPasswordHistory(
       String username, String password, String oldPassword, long timeToRecord) 
{
     InsertRowStatement insertRowStatement = new InsertRowStatement();
     try {
       insertRowStatement.setDevicePath(
           new PartialPath(SystemConstant.PREFIX_PASSWORD_HISTORY + ".`_" + 
username + "`"));
-      insertRowStatement.setTime(CommonDateTimeUtils.currentTime());
+      insertRowStatement.setTime(timeToRecord);
       insertRowStatement.setMeasurements(new String[] {"password", 
"oldPassword"});
       insertRowStatement.setValues(
           new Object[] {
@@ -195,4 +197,52 @@ public class DataNodeAuthUtils {
       }
     }
   }
+
+  public static TSStatus deletePasswordHistory(String username) {
+    DeleteTimeSeriesStatement deleteTimeSeriesStatement = new 
DeleteTimeSeriesStatement();
+    try {
+      PartialPath devicePath =
+          new PartialPath(SystemConstant.PREFIX_PASSWORD_HISTORY + ".`_" + 
username + "`");
+      deleteTimeSeriesStatement.setPathPatternList(
+          Arrays.asList(
+              devicePath.concatAsMeasurementPath("password"),
+              devicePath.concatAsMeasurementPath("oldPassword")));
+    } catch (IllegalPathException ignored) {
+      return new TSStatus(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
+          .setMessage(
+              "Cannot delete password history for "
+                  + username
+                  + " because the path will be illegal");
+    }
+
+    long queryId = -1;
+    try {
+      SessionInfo sessionInfo =
+          new SessionInfo(0, AuthorityChecker.SUPER_USER, 
ZoneId.systemDefault());
+
+      queryId = SessionManager.getInstance().requestQueryId();
+      ExecutionResult result =
+          Coordinator.getInstance()
+              .executeForTreeModel(
+                  deleteTimeSeriesStatement,
+                  queryId,
+                  sessionInfo,
+                  "",
+                  ClusterPartitionFetcher.getInstance(),
+                  ClusterSchemaFetcher.getInstance());
+      return result.status;
+    } catch (Exception e) {
+      if 
(CommonDescriptor.getInstance().getConfig().isMayBypassPasswordCheckInException())
 {
+        return StatusUtils.OK;
+      }
+      LOGGER.error("Cannot delete password history for {} because {}", 
username, e.getMessage());
+      return new TSStatus(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
+          .setMessage(
+              "The server is not ready for this operation, please check the 
server log for details");
+    } finally {
+      if (queryId != -1) {
+        Coordinator.getInstance().cleanupQueryExecution(queryId);
+      }
+    }
+  }
 }

Reply via email to