This is an automated email from the ASF dual-hosted git repository. jiangtian pushed a commit to branch fix_old_password_in_create in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 8b3eacd5d9409bd98df9785a48c9a917699f194e Author: Tian Jiang <[email protected]> AuthorDate: Mon Sep 15 14:41:28 2025 +0800 Fix that oldPassword is not recorded during user creation & delete password history after user removal --- .../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); + } + } + } }
