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 a835194b466 Fix query leak during login & fix inconsistent expiration
hints & skipped expiration check when password history is missing (#16166)
a835194b466 is described below
commit a835194b4662f4328f468e2b357f937f82ab6093
Author: Jiang Tian <[email protected]>
AuthorDate: Sat Aug 16 12:46:43 2025 +0800
Fix query leak during login & fix inconsistent expiration hints & skipped
expiration check when password history is missing (#16166)
* Fix query leak during login & fix inconsistent expiration hints
* fix test
* Fix ignoring expiration check when the password history is missing
* change ci jdk distribution
* revert somd jdks of ci work flow
* avoid using ClusterResultSetInTest
* fix session not open
* fix test
* spotless
---
.../org/apache/iotdb/db/it/IoTDBLoginAndOutIT.java | 87 ++++++++++++++++++++++
.../iotdb/db/protocol/session/SessionManager.java | 19 +++--
.../sql/ast/RelationalAuthorStatement.java | 9 ++-
.../plan/statement/sys/AuthorStatement.java | 9 ++-
.../apache/iotdb/db/utils/DataNodeAuthUtils.java | 17 ++++-
5 files changed, 129 insertions(+), 12 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoginAndOutIT.java
b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoginAndOutIT.java
new file mode 100644
index 00000000000..3d6f07681de
--- /dev/null
+++
b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBLoginAndOutIT.java
@@ -0,0 +1,87 @@
+/*
+ * 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.db.it;
+
+import org.apache.iotdb.isession.SessionDataSet;
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.env.cluster.node.DataNodeWrapper;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+import org.apache.iotdb.session.Session;
+
+import org.apache.tsfile.read.common.RowRecord;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBLoginAndOutIT {
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // use small page
+ EnvFactory.getEnv().initClusterEnvironment();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ }
+
+ @Test
+ public void testRepeatedlyLoginAndOut() throws Exception {
+ int attempts = 100;
+ for (int i = 0; i < attempts; i++) {
+ try (Connection ignored = EnvFactory.getEnv().getConnection()) {
+ // do nothing
+ }
+ }
+ DataNodeWrapper dataNodeWrapper =
EnvFactory.getEnv().getDataNodeWrapper(0);
+ try (Session session = new Session(dataNodeWrapper.getIp(),
dataNodeWrapper.getPort())) {
+ session.open();
+ int rowCount = 0;
+ SessionDataSet dataSet = session.executeQueryStatement("SHOW QUERIES");
+ int columnCount = dataSet.getColumnNames().size();
+ for (int i = 0; i < columnCount; i++) {
+ System.out.printf("%s, ", dataSet.getColumnNames().get(i));
+ }
+ System.out.println();
+ while (dataSet.hasNext()) {
+ RowRecord rec = dataSet.next();
+ System.out.printf("%s, ", rec.getTimestamp());
+ for (int i = 0; i < columnCount - 1; i++) {
+ System.out.printf("%s, ", rec.getFields().get(i).toString());
+ }
+ System.out.println();
+ rowCount++;
+ }
+ // show queries and show data nodes
+ assertTrue(rowCount <= 10);
+ }
+ }
+}
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 69008d34b75..9fa85a0be76 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
@@ -152,12 +152,14 @@ public class SessionManager implements
SessionManagerMBean {
lastDataQueryReq.setPaths(
Collections.singletonList(
SystemConstant.PREFIX_PASSWORD_HISTORY + ".`_" + username +
"`.password"));
+
+ long queryId = -1;
try {
Statement statement =
StatementGenerator.createStatement(lastDataQueryReq);
SessionInfo sessionInfo =
new SessionInfo(0, AuthorityChecker.SUPER_USER,
ZoneId.systemDefault());
- long queryId = requestQueryId();
+ queryId = requestQueryId();
ExecutionResult result =
Coordinator.getInstance()
.executeForTreeModel(
@@ -196,8 +198,9 @@ public class SessionManager implements SessionManagerMBean {
return lastPasswordTime + passwordExpirationDays * 1000 * 86400;
}
} else {
- // the password is incorrect, later logIn will fail
- return Long.MAX_VALUE;
+ // 1. the password is incorrect, later logIn will fail
+ // 2. the password history does not record correctly, use the
current time to create one
+ return null;
}
} else {
return null;
@@ -211,6 +214,10 @@ public class SessionManager implements SessionManagerMBean
{
"Internal server error " + ", please log in later or disable
password expiration.",
TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
}
+ } finally {
+ if (queryId != -1) {
+ Coordinator.getInstance().cleanupQueryExecution(queryId);
+ }
}
}
@@ -256,7 +263,9 @@ public class SessionManager implements SessionManagerMBean {
LOGGER.info(
"No password history for user {}, using the current time to
create a new one",
username);
- TSStatus tsStatus = DataNodeAuthUtils.recordPassword(username,
password, null);
+ long currentTime = CommonDateTimeUtils.currentTime();
+ TSStatus tsStatus =
+ DataNodeAuthUtils.recordPassword(username, password, null,
currentTime);
if (tsStatus.getCode() !=
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
openSessionResp
.sessionId(-1)
@@ -265,7 +274,7 @@ public class SessionManager implements SessionManagerMBean {
return openSessionResp;
}
timeToExpire =
- System.currentTimeMillis()
+ CommonDateTimeUtils.convertIoTDBTimeToMillis(currentTime)
+
CommonDescriptor.getInstance().getConfig().getPasswordExpirationDays()
* 1000
* 86400;
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 74454e0eaf3..4e2b4597672 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.CommonDateTimeUtils;
import org.apache.iotdb.db.queryengine.plan.analyze.QueryType;
import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType;
import org.apache.iotdb.db.utils.DataNodeAuthUtils;
@@ -265,7 +266,9 @@ public class RelationalAuthorStatement extends Statement {
}
private TSStatus onCreateUserSuccess() {
- TSStatus tsStatus = DataNodeAuthUtils.recordPassword(userName, password,
null);
+ TSStatus tsStatus =
+ DataNodeAuthUtils.recordPassword(
+ userName, password, null, CommonDateTimeUtils.currentTime());
try {
RpcUtils.verifySuccess(tsStatus);
} catch (StatementExecutionException e) {
@@ -275,7 +278,9 @@ public class RelationalAuthorStatement extends Statement {
}
private TSStatus onUpdateUserSuccess() {
- TSStatus tsStatus = DataNodeAuthUtils.recordPassword(userName, password,
oldPassword);
+ TSStatus tsStatus =
+ DataNodeAuthUtils.recordPassword(
+ userName, password, oldPassword,
CommonDateTimeUtils.currentTime());
try {
RpcUtils.verifySuccess(tsStatus);
} catch (StatementExecutionException e) {
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 fc0481fa656..1cc2b3d499c 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.CommonDateTimeUtils;
import org.apache.iotdb.db.auth.AuthorityChecker;
import org.apache.iotdb.db.queryengine.plan.analyze.QueryType;
import org.apache.iotdb.db.queryengine.plan.statement.AuthorType;
@@ -364,7 +365,9 @@ public class AuthorStatement extends Statement implements
IConfigStatement {
}
private TSStatus onCreateUserSuccess() {
- TSStatus tsStatus = DataNodeAuthUtils.recordPassword(userName, password,
null);
+ TSStatus tsStatus =
+ DataNodeAuthUtils.recordPassword(
+ userName, password, null, CommonDateTimeUtils.currentTime());
try {
RpcUtils.verifySuccess(tsStatus);
} catch (StatementExecutionException e) {
@@ -374,7 +377,9 @@ public class AuthorStatement extends Statement implements
IConfigStatement {
}
private TSStatus onUpdateUserSuccess() {
- TSStatus tsStatus = DataNodeAuthUtils.recordPassword(userName,
newPassword, password);
+ TSStatus tsStatus =
+ DataNodeAuthUtils.recordPassword(
+ userName, newPassword, password,
CommonDateTimeUtils.currentTime());
try {
RpcUtils.verifySuccess(tsStatus);
} catch (StatementExecutionException e) {
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 3526c2619ff..180fb3e3e68 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
@@ -65,6 +65,7 @@ public class DataNodeAuthUtils {
*/
public static long getPasswordChangeTimeMillis(String username, String
password) {
+ long queryId = -1;
try {
Statement statement =
StatementGenerator.createStatement(
@@ -79,7 +80,7 @@ public class DataNodeAuthUtils {
SessionInfo sessionInfo =
new SessionInfo(0, AuthorityChecker.SUPER_USER,
ZoneId.systemDefault());
- long queryId = SessionManager.getInstance().requestQueryId();
+ queryId = SessionManager.getInstance().requestQueryId();
ExecutionResult result =
Coordinator.getInstance()
.executeForTreeModel(
@@ -108,6 +109,10 @@ public class DataNodeAuthUtils {
}
} catch (IoTDBException e) {
LOGGER.warn("Cannot generate query for checking password reuse
interval", e);
+ } finally {
+ if (queryId != -1) {
+ Coordinator.getInstance().cleanupQueryExecution(queryId);
+ }
}
return -1;
}
@@ -139,7 +144,8 @@ public class DataNodeAuthUtils {
currentTimeMillis);
}
- public static TSStatus recordPassword(String username, String password,
String oldPassword) {
+ public static TSStatus recordPassword(
+ String username, String password, String oldPassword, long timeToRecord)
{
InsertRowStatement insertRowStatement = new InsertRowStatement();
try {
insertRowStatement.setDevicePath(
@@ -160,11 +166,12 @@ public class DataNodeAuthUtils {
+ " because the path will be illegal");
}
+ long queryId = -1;
try {
SessionInfo sessionInfo =
new SessionInfo(0, AuthorityChecker.SUPER_USER,
ZoneId.systemDefault());
- long queryId = SessionManager.getInstance().requestQueryId();
+ queryId = SessionManager.getInstance().requestQueryId();
ExecutionResult result =
Coordinator.getInstance()
.executeForTreeModel(
@@ -182,6 +189,10 @@ public class DataNodeAuthUtils {
LOGGER.error("Cannot create password history for {} because {}",
username, e.getMessage());
return new TSStatus(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
.setMessage("The server is not ready for login, please check the
server log for details");
+ } finally {
+ if (queryId != -1) {
+ Coordinator.getInstance().cleanupQueryExecution(queryId);
+ }
}
}
}