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 c4fc2e9c7bf Use userId to record password history (#16542)
c4fc2e9c7bf is described below
commit c4fc2e9c7bfa6310028212a10c3ad04165c75008
Author: Jiang Tian <[email protected]>
AuthorDate: Thu Oct 9 17:58:16 2025 +0800
Use userId to record password history (#16542)
---
.../org/apache/iotdb/db/it/auth/IoTDBAuthIT.java | 28 +++--
.../protocol/thrift/IoTDBDataNodeReceiver.java | 4 +-
.../iotdb/db/protocol/session/SessionManager.java | 114 +-----------------
.../execution/config/TableConfigTaskVisitor.java | 2 +-
.../execution/config/TreeConfigTaskVisitor.java | 3 +-
.../sql/ast/RelationalAuthorStatement.java | 17 ++-
.../plan/statement/sys/AuthorStatement.java | 17 ++-
.../apache/iotdb/db/utils/DataNodeAuthUtils.java | 128 ++++++++++++++++++---
8 files changed, 171 insertions(+), 142 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 3dc96baf232..6b8746bcad3 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
@@ -50,6 +50,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
+import static org.apache.iotdb.commons.auth.entity.User.INTERNAL_USER_END_ID;
import static org.apache.iotdb.db.audit.DNAuditLogger.PREFIX_PASSWORD_HISTORY;
import static org.apache.iotdb.db.it.utils.TestUtils.createUser;
import static org.apache.iotdb.db.it.utils.TestUtils.resultSetEqualTest;
@@ -1518,9 +1519,12 @@ public class IoTDBAuthIT {
public void testPasswordHistoryCreateAndDrop(Statement statement) throws
SQLException {
statement.execute("create user userA 'abcdef123456'");
+ long expectedUserAId = INTERNAL_USER_END_ID + 1;
try (ResultSet resultSet =
statement.executeQuery(
- String.format("select last password from %s.`_userA`",
PREFIX_PASSWORD_HISTORY))) {
+ String.format(
+ "select last password from %s.`_" + expectedUserAId + "`",
+ PREFIX_PASSWORD_HISTORY))) {
if (!resultSet.next()) {
fail("Password history not found");
}
@@ -1529,7 +1533,9 @@ public class IoTDBAuthIT {
try (ResultSet resultSet =
statement.executeQuery(
- String.format("select last oldPassword from %s.`_userA`",
PREFIX_PASSWORD_HISTORY))) {
+ String.format(
+ "select last oldPassword from %s.`_" + expectedUserAId + "`",
+ PREFIX_PASSWORD_HISTORY))) {
if (!resultSet.next()) {
fail("Password history not found");
}
@@ -1540,13 +1546,17 @@ public class IoTDBAuthIT {
try (ResultSet resultSet =
statement.executeQuery(
- String.format("select last password from %s.`_userA`",
PREFIX_PASSWORD_HISTORY))) {
+ String.format(
+ "select last password from %s.`_" + expectedUserAId + "`",
+ PREFIX_PASSWORD_HISTORY))) {
assertFalse(resultSet.next());
}
try (ResultSet resultSet =
statement.executeQuery(
- String.format("select last oldPassword from %s.`_userA`",
PREFIX_PASSWORD_HISTORY))) {
+ String.format(
+ "select last oldPassword from %s.`_" + expectedUserAId + "`",
+ PREFIX_PASSWORD_HISTORY))) {
assertFalse(resultSet.next());
}
}
@@ -1555,9 +1565,12 @@ public class IoTDBAuthIT {
statement.execute("create user userA 'abcdef123456'");
statement.execute("alter user userA set password 'abcdef654321'");
+ long expectedUserAId = INTERNAL_USER_END_ID + 2;
try (ResultSet resultSet =
statement.executeQuery(
- String.format("select last password from %s.`_userA`",
PREFIX_PASSWORD_HISTORY))) {
+ String.format(
+ "select last password from %s.`_" + expectedUserAId + "`",
+ PREFIX_PASSWORD_HISTORY))) {
if (!resultSet.next()) {
fail("Password history not found");
}
@@ -1567,14 +1580,15 @@ public class IoTDBAuthIT {
try (ResultSet resultSet =
statement.executeQuery(
String.format(
- "select oldPassword from %s.`_userA` order by time desc limit
1",
+ "select oldPassword from %s.`_" + expectedUserAId + "` order
by time desc limit 1",
PREFIX_PASSWORD_HISTORY))) {
if (!resultSet.next()) {
fail("Password history not found");
}
assertEquals(
AuthUtils.encryptPassword("abcdef123456"),
- resultSet.getString(String.format("%s._userA.oldPassword",
PREFIX_PASSWORD_HISTORY)));
+ resultSet.getString(
+ String.format("%s._" + expectedUserAId + ".oldPassword",
PREFIX_PASSWORD_HISTORY)));
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java
index 16f463f5f6f..e4093c26081 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java
@@ -103,6 +103,7 @@ import
org.apache.iotdb.db.storageengine.rescon.disk.FolderManager;
import
org.apache.iotdb.db.storageengine.rescon.disk.strategy.DirectoryStrategyType;
import org.apache.iotdb.db.tools.schema.SRStatementGenerator;
import org.apache.iotdb.db.tools.schema.SchemaRegionSnapshotParser;
+import org.apache.iotdb.db.utils.DataNodeAuthUtils;
import org.apache.iotdb.pipe.api.exception.PipeException;
import org.apache.iotdb.rpc.RpcUtils;
import org.apache.iotdb.rpc.TSStatusCode;
@@ -936,7 +937,8 @@ public class IoTDBDataNodeReceiver extends
IoTDBFileReceiver {
return RpcUtils.getStatus(openSessionResp.getCode(),
openSessionResp.getMessage());
}
- Long timeToExpire = SESSION_MANAGER.checkPasswordExpiration(username,
password);
+ long userId = AuthorityChecker.getUserId(username).orElse(-1L);
+ Long timeToExpire = DataNodeAuthUtils.checkPasswordExpiration(userId,
password);
if (timeToExpire != null && timeToExpire <= System.currentTimeMillis()) {
return RpcUtils.getStatus(
TSStatusCode.ILLEGAL_PASSWORD.getStatusCode(),
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 bebc93b7e6b..e1851f62dc6 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
@@ -26,28 +26,18 @@ import org.apache.iotdb.commons.audit.AuditLogOperation;
import org.apache.iotdb.commons.audit.UserEntity;
import org.apache.iotdb.commons.conf.CommonDescriptor;
import org.apache.iotdb.commons.conf.IoTDBConstant;
-import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
import org.apache.iotdb.commons.service.JMXService;
import org.apache.iotdb.commons.service.ServiceType;
import org.apache.iotdb.commons.service.metric.MetricService;
import org.apache.iotdb.commons.service.metric.enums.Metric;
import org.apache.iotdb.commons.service.metric.enums.Tag;
-import org.apache.iotdb.commons.utils.AuthUtils;
import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
import org.apache.iotdb.db.audit.DNAuditLogger;
import org.apache.iotdb.db.auth.AuthorityChecker;
import org.apache.iotdb.db.auth.LoginLockManager;
-import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.protocol.basic.BasicOpenSessionResp;
import org.apache.iotdb.db.protocol.thrift.OperationType;
import org.apache.iotdb.db.queryengine.common.SessionInfo;
-import org.apache.iotdb.db.queryengine.plan.Coordinator;
-import org.apache.iotdb.db.queryengine.plan.analyze.ClusterPartitionFetcher;
-import
org.apache.iotdb.db.queryengine.plan.analyze.schema.ClusterSchemaFetcher;
-import org.apache.iotdb.db.queryengine.plan.execution.ExecutionResult;
-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.storageengine.dataregion.read.control.QueryResourceManager;
import org.apache.iotdb.db.utils.DataNodeAuthUtils;
import org.apache.iotdb.metrics.utils.MetricLevel;
@@ -56,11 +46,9 @@ import org.apache.iotdb.rpc.RpcUtils;
import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.service.rpc.thrift.TSConnectionInfo;
import org.apache.iotdb.service.rpc.thrift.TSConnectionInfoResp;
-import org.apache.iotdb.service.rpc.thrift.TSLastDataQueryReq;
import org.apache.iotdb.service.rpc.thrift.TSProtocolVersion;
import org.apache.commons.lang3.StringUtils;
-import org.apache.tsfile.read.common.block.TsBlock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -68,10 +56,8 @@ import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
-import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
@@ -132,99 +118,6 @@ public class SessionManager implements SessionManagerMBean
{
IClientSession.SqlDialect.TREE);
}
- /**
- * Check if the password for the give user has expired.
- *
- * @return the timestamp when the password will expire. Long.MAX if the
password never expires.
- * Null if the password history cannot be found.
- */
- public Long checkPasswordExpiration(String username, String password) {
- // check password expiration
- long passwordExpirationDays =
- CommonDescriptor.getInstance().getConfig().getPasswordExpirationDays();
- boolean mayBypassPasswordCheckInException =
-
CommonDescriptor.getInstance().getConfig().isMayBypassPasswordCheckInException();
-
- TSLastDataQueryReq lastDataQueryReq = new TSLastDataQueryReq();
- lastDataQueryReq.setSessionId(0);
- lastDataQueryReq.setPaths(
- Collections.singletonList(
- DNAuditLogger.PREFIX_PASSWORD_HISTORY + ".`_" + username +
"`.password"));
-
- long queryId = -1;
- try {
- Statement statement =
StatementGenerator.createStatement(lastDataQueryReq);
- SessionInfo sessionInfo =
- new SessionInfo(
- 0,
- new UserEntity(
- AuthorityChecker.INTERNAL_AUDIT_USER_ID,
- AuthorityChecker.INTERNAL_AUDIT_USER,
-
IoTDBDescriptor.getInstance().getConfig().getInternalAddress()),
- ZoneId.systemDefault());
-
- queryId = requestQueryId();
- ExecutionResult result =
- Coordinator.getInstance()
- .executeForTreeModel(
- statement,
- queryId,
- sessionInfo,
- "",
- ClusterPartitionFetcher.getInstance(),
- ClusterSchemaFetcher.getInstance());
- if (result.status.getCode() !=
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
- LOGGER.warn("Fail to check password expiration: {}", result.status);
- throw new IoTDBRuntimeException(
- "Cannot query password history because: "
- + result
- + ", please log in later or disable password expiration.",
- result.status.getCode());
- }
-
- IQueryExecution queryExecution =
Coordinator.getInstance().getQueryExecution(queryId);
- Optional<TsBlock> batchResult = queryExecution.getBatchResult();
- if (batchResult.isPresent()) {
- TsBlock tsBlock = batchResult.get();
- if (tsBlock.getPositionCount() <= 0) {
- // no password history, may have upgraded from an older version
- return null;
- }
- long lastPasswordTime =
-
CommonDateTimeUtils.convertIoTDBTimeToMillis(tsBlock.getTimeByIndex(0));
- // columns of last query: [timeseriesName, value, dataType]
- String oldPassword = tsBlock.getColumn(1).getBinary(0).toString();
- if (oldPassword.equals(AuthUtils.encryptPassword(password))) {
- if (lastPasswordTime + passwordExpirationDays * 1000 * 86400 <=
lastPasswordTime) {
- // overflow or passwordExpirationDays <= 0
- return Long.MAX_VALUE;
- } else {
- return lastPasswordTime + passwordExpirationDays * 1000 * 86400;
- }
- } else {
- // 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;
- }
- } catch (Throwable e) {
- LOGGER.error("Fail to check password expiration", e);
- if (mayBypassPasswordCheckInException) {
- return Long.MAX_VALUE;
- } else {
- throw new IoTDBRuntimeException(
- "Internal server error " + ", please log in later or disable
password expiration.",
- TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
- }
- } finally {
- if (queryId != -1) {
- Coordinator.getInstance().cleanupQueryExecution(queryId);
- }
- }
- }
-
public BasicOpenSessionResp login(
IClientSession session,
String username,
@@ -235,7 +128,9 @@ public class SessionManager implements SessionManagerMBean {
IClientSession.SqlDialect sqlDialect) {
BasicOpenSessionResp openSessionResp = new BasicOpenSessionResp();
- Long timeToExpire = checkPasswordExpiration(username, password);
+ long userId = AuthorityChecker.getUserId(username).orElse(-1L);
+
+ Long timeToExpire = DataNodeAuthUtils.checkPasswordExpiration(userId,
password);
if (timeToExpire != null && timeToExpire <= System.currentTimeMillis()) {
openSessionResp
.sessionId(-1)
@@ -244,7 +139,6 @@ public class SessionManager implements SessionManagerMBean {
return openSessionResp;
}
- long userId = AuthorityChecker.getUserId(username).orElse(-1L);
boolean enableLoginLock = userId != -1;
LoginLockManager loginLockManager = LoginLockManager.getInstance();
if (enableLoginLock && loginLockManager.checkLock(userId,
session.getClientAddress())) {
@@ -281,7 +175,7 @@ public class SessionManager implements SessionManagerMBean {
username);
long currentTime = CommonDateTimeUtils.currentTime();
TSStatus tsStatus =
- DataNodeAuthUtils.recordPasswordHistory(username, password,
password, currentTime);
+ DataNodeAuthUtils.recordPasswordHistory(userId, 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/execution/config/TableConfigTaskVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
index 57b4d5bbdae..0475a27c26d 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
@@ -1406,7 +1406,7 @@ public class TableConfigTaskVisitor extends
AstVisitor<IConfigTask, MPPQueryCont
throw new SemanticException("User " + node.getUserName() + " not found");
}
node.setOldPassword(user.getPassword());
- DataNodeAuthUtils.verifyPasswordReuse(node.getUserName(),
node.getPassword());
+ DataNodeAuthUtils.verifyPasswordReuse(node.getAssociatedUserId(),
node.getPassword());
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java
index e01dfa918db..465326ff639 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java
@@ -332,7 +332,8 @@ public class TreeConfigTaskVisitor extends
StatementVisitor<IConfigTask, MPPQuer
throw new SemanticException("User " + statement.getUserName() + " not
found");
}
statement.setPassWord(user.getPassword());
- DataNodeAuthUtils.verifyPasswordReuse(statement.getUserName(),
statement.getNewPassword());
+ DataNodeAuthUtils.verifyPasswordReuse(
+ statement.getAssociatedUsedId(), statement.getNewPassword());
}
private void visitRenameUser(AuthorStatement statement) {
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 74e93acaad0..16f38b9c684 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
@@ -57,6 +57,9 @@ public class RelationalAuthorStatement extends Statement {
private String newUsername = "";
private String loginAddr;
+ // the id of userName
+ private long associatedUserId = -1;
+
public RelationalAuthorStatement(
AuthorRType authorType,
String userName,
@@ -162,6 +165,9 @@ public class RelationalAuthorStatement extends Statement {
public void setUserName(String userName) {
this.userName = userName;
+ if (authorType != AuthorRType.CREATE_USER) {
+ this.associatedUserId = AuthorityChecker.getUserId(userName).orElse(-1L);
+ }
}
public void setRoleName(String roleName) {
@@ -301,11 +307,12 @@ public class RelationalAuthorStatement extends Statement {
}
private TSStatus onCreateUserSuccess() {
+ associatedUserId = AuthorityChecker.getUserId(userName).orElse(-1L);
// the old password is expected to be encrypted during updates, so we also
encrypt it here to
// keep consistency
TSStatus tsStatus =
DataNodeAuthUtils.recordPasswordHistory(
- userName,
+ associatedUserId,
password,
AuthUtils.encryptPassword(password),
CommonDateTimeUtils.currentTime());
@@ -320,7 +327,7 @@ public class RelationalAuthorStatement extends Statement {
private TSStatus onUpdateUserSuccess() {
TSStatus tsStatus =
DataNodeAuthUtils.recordPasswordHistory(
- userName, password, oldPassword,
CommonDateTimeUtils.currentTime());
+ associatedUserId, password, oldPassword,
CommonDateTimeUtils.currentTime());
try {
RpcUtils.verifySuccess(tsStatus);
} catch (StatementExecutionException e) {
@@ -330,7 +337,7 @@ public class RelationalAuthorStatement extends Statement {
}
private TSStatus onDropUserSuccess() {
- TSStatus tsStatus = DataNodeAuthUtils.deletePasswordHistory(userName);
+ TSStatus tsStatus =
DataNodeAuthUtils.deletePasswordHistory(associatedUserId);
try {
RpcUtils.verifySuccess(tsStatus);
} catch (StatementExecutionException e) {
@@ -435,4 +442,8 @@ public class RelationalAuthorStatement extends Statement {
}
return RpcUtils.SUCCESS_STATUS;
}
+
+ public long getAssociatedUserId() {
+ return associatedUserId;
+ }
}
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 afbef6f7900..a972ddea232 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
@@ -51,6 +51,9 @@ public class AuthorStatement extends Statement implements
IConfigStatement {
private String newUsername = "";
private String loginAddr;
+ // the id of userName
+ private long associatedUsedId = -1;
+
/**
* Constructor with AuthorType.
*
@@ -137,6 +140,9 @@ public class AuthorStatement extends Statement implements
IConfigStatement {
public void setUserName(String userName) {
this.userName = userName;
+ if (authorType != AuthorType.CREATE_USER) {
+ this.associatedUsedId = AuthorityChecker.getUserId(userName).orElse(-1L);
+ }
}
public String getRoleName() {
@@ -269,11 +275,12 @@ public class AuthorStatement extends Statement implements
IConfigStatement {
}
private TSStatus onCreateUserSuccess() {
+ associatedUsedId = AuthorityChecker.getUserId(userName).orElse(-1L);
// the old password is expected to be encrypted during updates, so we also
encrypt it here to
// keep consistency
TSStatus tsStatus =
DataNodeAuthUtils.recordPasswordHistory(
- userName,
+ associatedUsedId,
password,
AuthUtils.encryptPassword(password),
CommonDateTimeUtils.currentTime());
@@ -288,7 +295,7 @@ public class AuthorStatement extends Statement implements
IConfigStatement {
private TSStatus onUpdateUserSuccess() {
TSStatus tsStatus =
DataNodeAuthUtils.recordPasswordHistory(
- userName, newPassword, password,
CommonDateTimeUtils.currentTime());
+ associatedUsedId, newPassword, password,
CommonDateTimeUtils.currentTime());
try {
RpcUtils.verifySuccess(tsStatus);
} catch (StatementExecutionException e) {
@@ -298,7 +305,7 @@ public class AuthorStatement extends Statement implements
IConfigStatement {
}
private TSStatus onDropUserSuccess() {
- TSStatus tsStatus = DataNodeAuthUtils.deletePasswordHistory(userName);
+ TSStatus tsStatus =
DataNodeAuthUtils.deletePasswordHistory(associatedUsedId);
try {
RpcUtils.verifySuccess(tsStatus);
} catch (StatementExecutionException e) {
@@ -337,4 +344,8 @@ public class AuthorStatement extends Statement implements
IConfigStatement {
}
return RpcUtils.SUCCESS_STATUS;
}
+
+ public long getAssociatedUsedId() {
+ return associatedUsedId;
+ }
}
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 39af6bf24bb..3a96f72f033 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
@@ -24,6 +24,7 @@ import org.apache.iotdb.commons.audit.UserEntity;
import org.apache.iotdb.commons.conf.CommonDescriptor;
import org.apache.iotdb.commons.exception.IllegalPathException;
import org.apache.iotdb.commons.exception.IoTDBException;
+import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.utils.AuthUtils;
import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
@@ -44,6 +45,7 @@ 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.iotdb.service.rpc.thrift.TSLastDataQueryReq;
import org.apache.tsfile.enums.TSDataType;
import org.apache.tsfile.read.common.block.TsBlock;
@@ -54,6 +56,7 @@ import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.time.ZoneId;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
import java.util.Optional;
@@ -67,7 +70,7 @@ public class DataNodeAuthUtils {
* @return the timestamp when the password of the user is lastly changed
from the given one to a
* new one, or -1 if the password has not been changed.
*/
- public static long getPasswordChangeTimeMillis(String username, String
password) {
+ public static long getPasswordChangeTimeMillis(long userId, String password)
{
long queryId = -1;
try {
@@ -76,7 +79,7 @@ public class DataNodeAuthUtils {
"SELECT password from "
+ DNAuditLogger.PREFIX_PASSWORD_HISTORY
+ ".`_"
- + username
+ + userId
+ "` where oldPassword='"
+ AuthUtils.encryptPassword(password)
+ "' order by time desc limit 1",
@@ -128,14 +131,14 @@ public class DataNodeAuthUtils {
return -1;
}
- public static void verifyPasswordReuse(String username, String password) {
+ public static void verifyPasswordReuse(long userId, String password) {
long passwordReuseIntervalDays =
CommonDescriptor.getInstance().getConfig().getPasswordReuseIntervalDays();
if (password == null || passwordReuseIntervalDays <= 0) {
return;
}
- long passwordChangeTime =
DataNodeAuthUtils.getPasswordChangeTimeMillis(username, password);
+ long passwordChangeTime =
DataNodeAuthUtils.getPasswordChangeTimeMillis(userId, password);
long currentTimeMillis = System.currentTimeMillis();
long elapsedTime = currentTimeMillis - passwordChangeTime;
long reuseIntervalMillis =
@@ -156,11 +159,11 @@ public class DataNodeAuthUtils {
}
public static TSStatus recordPasswordHistory(
- String username, String password, String oldPassword, long timeToRecord)
{
+ long userId, String password, String oldPassword, long timeToRecord) {
InsertRowStatement insertRowStatement = new InsertRowStatement();
try {
insertRowStatement.setDevicePath(
- new PartialPath(DNAuditLogger.PREFIX_PASSWORD_HISTORY + ".`_" +
username + "`"));
+ new PartialPath(DNAuditLogger.PREFIX_PASSWORD_HISTORY + ".`_" +
userId + "`"));
insertRowStatement.setTime(timeToRecord);
insertRowStatement.setMeasurements(new String[] {"password",
"oldPassword"});
insertRowStatement.setValues(
@@ -172,9 +175,7 @@ public class DataNodeAuthUtils {
} catch (IllegalPathException ignored) {
return new TSStatus(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
.setMessage(
- "Cannot create password history for "
- + username
- + " because the path will be illegal");
+ "Cannot create password history for " + userId + " because the
path will be illegal");
}
long queryId = -1;
@@ -203,7 +204,7 @@ public class DataNodeAuthUtils {
if
(CommonDescriptor.getInstance().getConfig().isMayBypassPasswordCheckInException())
{
return StatusUtils.OK;
}
- LOGGER.error("Cannot create password history for {} because {}",
username, e.getMessage());
+ LOGGER.error("Cannot create password history for {}", userId, e);
return new TSStatus(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
.setMessage("The server is not ready for login, please check the
server log for details");
} finally {
@@ -213,11 +214,11 @@ public class DataNodeAuthUtils {
}
}
- public static TSStatus deletePasswordHistory(String username) {
+ public static TSStatus deletePasswordHistory(long userId) {
DeleteTimeSeriesStatement deleteTimeSeriesStatement = new
DeleteTimeSeriesStatement();
try {
PartialPath devicePath =
- new PartialPath(DNAuditLogger.PREFIX_PASSWORD_HISTORY + ".`_" +
username + "`");
+ new PartialPath(DNAuditLogger.PREFIX_PASSWORD_HISTORY + ".`_" +
userId + "`");
deleteTimeSeriesStatement.setPathPatternList(
Arrays.asList(
devicePath.concatAsMeasurementPath("password"),
@@ -225,9 +226,7 @@ public class DataNodeAuthUtils {
} catch (IllegalPathException ignored) {
return new TSStatus(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
.setMessage(
- "Cannot delete password history for "
- + username
- + " because the path will be illegal");
+ "Cannot delete password history for " + userId + " because the
path will be illegal");
}
long queryId = -1;
@@ -256,7 +255,7 @@ public class DataNodeAuthUtils {
if
(CommonDescriptor.getInstance().getConfig().isMayBypassPasswordCheckInException())
{
return StatusUtils.OK;
}
- LOGGER.error("Cannot delete password history for {} because {}",
username, e.getMessage());
+ LOGGER.error("Cannot delete password history for {}", userId, e);
return new TSStatus(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
.setMessage(
"The server is not ready for this operation, please check the
server log for details");
@@ -266,4 +265,101 @@ public class DataNodeAuthUtils {
}
}
}
+
+ /**
+ * Check if the password for the give user has expired.
+ *
+ * @return the timestamp when the password will expire. Long.MAX if the
password never expires.
+ * Null if the password history cannot be found.
+ */
+ public static Long checkPasswordExpiration(long userId, String password) {
+ if (userId == -1) {
+ return null;
+ }
+
+ // check password expiration
+ long passwordExpirationDays =
+ CommonDescriptor.getInstance().getConfig().getPasswordExpirationDays();
+ boolean mayBypassPasswordCheckInException =
+
CommonDescriptor.getInstance().getConfig().isMayBypassPasswordCheckInException();
+
+ TSLastDataQueryReq lastDataQueryReq = new TSLastDataQueryReq();
+ lastDataQueryReq.setSessionId(0);
+ lastDataQueryReq.setPaths(
+ Collections.singletonList(
+ DNAuditLogger.PREFIX_PASSWORD_HISTORY + ".`_" + userId +
"`.password"));
+
+ long queryId = -1;
+ try {
+ Statement statement =
StatementGenerator.createStatement(lastDataQueryReq);
+ SessionInfo sessionInfo =
+ new SessionInfo(
+ 0,
+ new UserEntity(
+ AuthorityChecker.INTERNAL_AUDIT_USER_ID,
+ AuthorityChecker.INTERNAL_AUDIT_USER,
+
IoTDBDescriptor.getInstance().getConfig().getInternalAddress()),
+ ZoneId.systemDefault());
+
+ queryId = SessionManager.getInstance().requestQueryId();
+ ExecutionResult result =
+ Coordinator.getInstance()
+ .executeForTreeModel(
+ statement,
+ queryId,
+ sessionInfo,
+ "",
+ ClusterPartitionFetcher.getInstance(),
+ ClusterSchemaFetcher.getInstance());
+ if (result.status.getCode() !=
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+ LOGGER.warn("Fail to check password expiration: {}", result.status);
+ throw new IoTDBRuntimeException(
+ "Cannot query password history because: "
+ + result
+ + ", please log in later or disable password expiration.",
+ result.status.getCode());
+ }
+
+ IQueryExecution queryExecution =
Coordinator.getInstance().getQueryExecution(queryId);
+ Optional<TsBlock> batchResult = queryExecution.getBatchResult();
+ if (batchResult.isPresent()) {
+ TsBlock tsBlock = batchResult.get();
+ if (tsBlock.getPositionCount() <= 0) {
+ // no password history, may have upgraded from an older version
+ return null;
+ }
+ long lastPasswordTime =
+
CommonDateTimeUtils.convertIoTDBTimeToMillis(tsBlock.getTimeByIndex(0));
+ // columns of last query: [timeseriesName, value, dataType]
+ String oldPassword = tsBlock.getColumn(1).getBinary(0).toString();
+ if (oldPassword.equals(AuthUtils.encryptPassword(password))) {
+ if (lastPasswordTime + passwordExpirationDays * 1000 * 86400 <=
lastPasswordTime) {
+ // overflow or passwordExpirationDays <= 0
+ return Long.MAX_VALUE;
+ } else {
+ return lastPasswordTime + passwordExpirationDays * 1000 * 86400;
+ }
+ } else {
+ // 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;
+ }
+ } catch (Throwable e) {
+ LOGGER.error("Fail to check password expiration", e);
+ if (mayBypassPasswordCheckInException) {
+ return Long.MAX_VALUE;
+ } else {
+ throw new IoTDBRuntimeException(
+ "Internal server error " + ", please log in later or disable
password expiration.",
+ TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
+ }
+ } finally {
+ if (queryId != -1) {
+ Coordinator.getInstance().cleanupQueryExecution(queryId);
+ }
+ }
+ }
}