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);
+ }
+ }
+ }
}