Skip to content

Commit c4fc2e9

Browse files
authored
Use userId to record password history (apache#16542)
1 parent 080a810 commit c4fc2e9

File tree

8 files changed

+171
-142
lines changed

8 files changed

+171
-142
lines changed

integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import java.util.Set;
5151
import java.util.concurrent.Callable;
5252

53+
import static org.apache.iotdb.commons.auth.entity.User.INTERNAL_USER_END_ID;
5354
import static org.apache.iotdb.db.audit.DNAuditLogger.PREFIX_PASSWORD_HISTORY;
5455
import static org.apache.iotdb.db.it.utils.TestUtils.createUser;
5556
import static org.apache.iotdb.db.it.utils.TestUtils.resultSetEqualTest;
@@ -1518,9 +1519,12 @@ public void testPasswordHistory() {
15181519
public void testPasswordHistoryCreateAndDrop(Statement statement) throws SQLException {
15191520
statement.execute("create user userA 'abcdef123456'");
15201521

1522+
long expectedUserAId = INTERNAL_USER_END_ID + 1;
15211523
try (ResultSet resultSet =
15221524
statement.executeQuery(
1523-
String.format("select last password from %s.`_userA`", PREFIX_PASSWORD_HISTORY))) {
1525+
String.format(
1526+
"select last password from %s.`_" + expectedUserAId + "`",
1527+
PREFIX_PASSWORD_HISTORY))) {
15241528
if (!resultSet.next()) {
15251529
fail("Password history not found");
15261530
}
@@ -1529,7 +1533,9 @@ public void testPasswordHistoryCreateAndDrop(Statement statement) throws SQLExce
15291533

15301534
try (ResultSet resultSet =
15311535
statement.executeQuery(
1532-
String.format("select last oldPassword from %s.`_userA`", PREFIX_PASSWORD_HISTORY))) {
1536+
String.format(
1537+
"select last oldPassword from %s.`_" + expectedUserAId + "`",
1538+
PREFIX_PASSWORD_HISTORY))) {
15331539
if (!resultSet.next()) {
15341540
fail("Password history not found");
15351541
}
@@ -1540,13 +1546,17 @@ public void testPasswordHistoryCreateAndDrop(Statement statement) throws SQLExce
15401546

15411547
try (ResultSet resultSet =
15421548
statement.executeQuery(
1543-
String.format("select last password from %s.`_userA`", PREFIX_PASSWORD_HISTORY))) {
1549+
String.format(
1550+
"select last password from %s.`_" + expectedUserAId + "`",
1551+
PREFIX_PASSWORD_HISTORY))) {
15441552
assertFalse(resultSet.next());
15451553
}
15461554

15471555
try (ResultSet resultSet =
15481556
statement.executeQuery(
1549-
String.format("select last oldPassword from %s.`_userA`", PREFIX_PASSWORD_HISTORY))) {
1557+
String.format(
1558+
"select last oldPassword from %s.`_" + expectedUserAId + "`",
1559+
PREFIX_PASSWORD_HISTORY))) {
15501560
assertFalse(resultSet.next());
15511561
}
15521562
}
@@ -1555,9 +1565,12 @@ public void testPasswordHistoryAlter(Statement statement) throws SQLException {
15551565
statement.execute("create user userA 'abcdef123456'");
15561566
statement.execute("alter user userA set password 'abcdef654321'");
15571567

1568+
long expectedUserAId = INTERNAL_USER_END_ID + 2;
15581569
try (ResultSet resultSet =
15591570
statement.executeQuery(
1560-
String.format("select last password from %s.`_userA`", PREFIX_PASSWORD_HISTORY))) {
1571+
String.format(
1572+
"select last password from %s.`_" + expectedUserAId + "`",
1573+
PREFIX_PASSWORD_HISTORY))) {
15611574
if (!resultSet.next()) {
15621575
fail("Password history not found");
15631576
}
@@ -1567,14 +1580,15 @@ public void testPasswordHistoryAlter(Statement statement) throws SQLException {
15671580
try (ResultSet resultSet =
15681581
statement.executeQuery(
15691582
String.format(
1570-
"select oldPassword from %s.`_userA` order by time desc limit 1",
1583+
"select oldPassword from %s.`_" + expectedUserAId + "` order by time desc limit 1",
15711584
PREFIX_PASSWORD_HISTORY))) {
15721585
if (!resultSet.next()) {
15731586
fail("Password history not found");
15741587
}
15751588
assertEquals(
15761589
AuthUtils.encryptPassword("abcdef123456"),
1577-
resultSet.getString(String.format("%s._userA.oldPassword", PREFIX_PASSWORD_HISTORY)));
1590+
resultSet.getString(
1591+
String.format("%s._" + expectedUserAId + ".oldPassword", PREFIX_PASSWORD_HISTORY)));
15781592
}
15791593
}
15801594

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
import org.apache.iotdb.db.storageengine.rescon.disk.strategy.DirectoryStrategyType;
104104
import org.apache.iotdb.db.tools.schema.SRStatementGenerator;
105105
import org.apache.iotdb.db.tools.schema.SchemaRegionSnapshotParser;
106+
import org.apache.iotdb.db.utils.DataNodeAuthUtils;
106107
import org.apache.iotdb.pipe.api.exception.PipeException;
107108
import org.apache.iotdb.rpc.RpcUtils;
108109
import org.apache.iotdb.rpc.TSStatusCode;
@@ -936,7 +937,8 @@ protected TSStatus login() {
936937
return RpcUtils.getStatus(openSessionResp.getCode(), openSessionResp.getMessage());
937938
}
938939

939-
Long timeToExpire = SESSION_MANAGER.checkPasswordExpiration(username, password);
940+
long userId = AuthorityChecker.getUserId(username).orElse(-1L);
941+
Long timeToExpire = DataNodeAuthUtils.checkPasswordExpiration(userId, password);
940942
if (timeToExpire != null && timeToExpire <= System.currentTimeMillis()) {
941943
return RpcUtils.getStatus(
942944
TSStatusCode.ILLEGAL_PASSWORD.getStatusCode(),

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/session/SessionManager.java

Lines changed: 4 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,18 @@
2626
import org.apache.iotdb.commons.audit.UserEntity;
2727
import org.apache.iotdb.commons.conf.CommonDescriptor;
2828
import org.apache.iotdb.commons.conf.IoTDBConstant;
29-
import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
3029
import org.apache.iotdb.commons.service.JMXService;
3130
import org.apache.iotdb.commons.service.ServiceType;
3231
import org.apache.iotdb.commons.service.metric.MetricService;
3332
import org.apache.iotdb.commons.service.metric.enums.Metric;
3433
import org.apache.iotdb.commons.service.metric.enums.Tag;
35-
import org.apache.iotdb.commons.utils.AuthUtils;
3634
import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
3735
import org.apache.iotdb.db.audit.DNAuditLogger;
3836
import org.apache.iotdb.db.auth.AuthorityChecker;
3937
import org.apache.iotdb.db.auth.LoginLockManager;
40-
import org.apache.iotdb.db.conf.IoTDBDescriptor;
4138
import org.apache.iotdb.db.protocol.basic.BasicOpenSessionResp;
4239
import org.apache.iotdb.db.protocol.thrift.OperationType;
4340
import org.apache.iotdb.db.queryengine.common.SessionInfo;
44-
import org.apache.iotdb.db.queryengine.plan.Coordinator;
45-
import org.apache.iotdb.db.queryengine.plan.analyze.ClusterPartitionFetcher;
46-
import org.apache.iotdb.db.queryengine.plan.analyze.schema.ClusterSchemaFetcher;
47-
import org.apache.iotdb.db.queryengine.plan.execution.ExecutionResult;
48-
import org.apache.iotdb.db.queryengine.plan.execution.IQueryExecution;
49-
import org.apache.iotdb.db.queryengine.plan.parser.StatementGenerator;
50-
import org.apache.iotdb.db.queryengine.plan.statement.Statement;
5141
import org.apache.iotdb.db.storageengine.dataregion.read.control.QueryResourceManager;
5242
import org.apache.iotdb.db.utils.DataNodeAuthUtils;
5343
import org.apache.iotdb.metrics.utils.MetricLevel;
@@ -56,22 +46,18 @@
5646
import org.apache.iotdb.rpc.TSStatusCode;
5747
import org.apache.iotdb.service.rpc.thrift.TSConnectionInfo;
5848
import org.apache.iotdb.service.rpc.thrift.TSConnectionInfoResp;
59-
import org.apache.iotdb.service.rpc.thrift.TSLastDataQueryReq;
6049
import org.apache.iotdb.service.rpc.thrift.TSProtocolVersion;
6150

6251
import org.apache.commons.lang3.StringUtils;
63-
import org.apache.tsfile.read.common.block.TsBlock;
6452
import org.slf4j.Logger;
6553
import org.slf4j.LoggerFactory;
6654

6755
import java.time.Instant;
6856
import java.time.LocalDateTime;
6957
import java.time.ZoneId;
7058
import java.time.format.DateTimeFormatter;
71-
import java.util.Collections;
7259
import java.util.Comparator;
7360
import java.util.Map;
74-
import java.util.Optional;
7561
import java.util.Set;
7662
import java.util.TimeZone;
7763
import java.util.concurrent.ConcurrentHashMap;
@@ -132,99 +118,6 @@ public BasicOpenSessionResp login(
132118
IClientSession.SqlDialect.TREE);
133119
}
134120

135-
/**
136-
* Check if the password for the give user has expired.
137-
*
138-
* @return the timestamp when the password will expire. Long.MAX if the password never expires.
139-
* Null if the password history cannot be found.
140-
*/
141-
public Long checkPasswordExpiration(String username, String password) {
142-
// check password expiration
143-
long passwordExpirationDays =
144-
CommonDescriptor.getInstance().getConfig().getPasswordExpirationDays();
145-
boolean mayBypassPasswordCheckInException =
146-
CommonDescriptor.getInstance().getConfig().isMayBypassPasswordCheckInException();
147-
148-
TSLastDataQueryReq lastDataQueryReq = new TSLastDataQueryReq();
149-
lastDataQueryReq.setSessionId(0);
150-
lastDataQueryReq.setPaths(
151-
Collections.singletonList(
152-
DNAuditLogger.PREFIX_PASSWORD_HISTORY + ".`_" + username + "`.password"));
153-
154-
long queryId = -1;
155-
try {
156-
Statement statement = StatementGenerator.createStatement(lastDataQueryReq);
157-
SessionInfo sessionInfo =
158-
new SessionInfo(
159-
0,
160-
new UserEntity(
161-
AuthorityChecker.INTERNAL_AUDIT_USER_ID,
162-
AuthorityChecker.INTERNAL_AUDIT_USER,
163-
IoTDBDescriptor.getInstance().getConfig().getInternalAddress()),
164-
ZoneId.systemDefault());
165-
166-
queryId = requestQueryId();
167-
ExecutionResult result =
168-
Coordinator.getInstance()
169-
.executeForTreeModel(
170-
statement,
171-
queryId,
172-
sessionInfo,
173-
"",
174-
ClusterPartitionFetcher.getInstance(),
175-
ClusterSchemaFetcher.getInstance());
176-
if (result.status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
177-
LOGGER.warn("Fail to check password expiration: {}", result.status);
178-
throw new IoTDBRuntimeException(
179-
"Cannot query password history because: "
180-
+ result
181-
+ ", please log in later or disable password expiration.",
182-
result.status.getCode());
183-
}
184-
185-
IQueryExecution queryExecution = Coordinator.getInstance().getQueryExecution(queryId);
186-
Optional<TsBlock> batchResult = queryExecution.getBatchResult();
187-
if (batchResult.isPresent()) {
188-
TsBlock tsBlock = batchResult.get();
189-
if (tsBlock.getPositionCount() <= 0) {
190-
// no password history, may have upgraded from an older version
191-
return null;
192-
}
193-
long lastPasswordTime =
194-
CommonDateTimeUtils.convertIoTDBTimeToMillis(tsBlock.getTimeByIndex(0));
195-
// columns of last query: [timeseriesName, value, dataType]
196-
String oldPassword = tsBlock.getColumn(1).getBinary(0).toString();
197-
if (oldPassword.equals(AuthUtils.encryptPassword(password))) {
198-
if (lastPasswordTime + passwordExpirationDays * 1000 * 86400 <= lastPasswordTime) {
199-
// overflow or passwordExpirationDays <= 0
200-
return Long.MAX_VALUE;
201-
} else {
202-
return lastPasswordTime + passwordExpirationDays * 1000 * 86400;
203-
}
204-
} else {
205-
// 1. the password is incorrect, later logIn will fail
206-
// 2. the password history does not record correctly, use the current time to create one
207-
return null;
208-
}
209-
} else {
210-
return null;
211-
}
212-
} catch (Throwable e) {
213-
LOGGER.error("Fail to check password expiration", e);
214-
if (mayBypassPasswordCheckInException) {
215-
return Long.MAX_VALUE;
216-
} else {
217-
throw new IoTDBRuntimeException(
218-
"Internal server error " + ", please log in later or disable password expiration.",
219-
TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
220-
}
221-
} finally {
222-
if (queryId != -1) {
223-
Coordinator.getInstance().cleanupQueryExecution(queryId);
224-
}
225-
}
226-
}
227-
228121
public BasicOpenSessionResp login(
229122
IClientSession session,
230123
String username,
@@ -235,7 +128,9 @@ public BasicOpenSessionResp login(
235128
IClientSession.SqlDialect sqlDialect) {
236129
BasicOpenSessionResp openSessionResp = new BasicOpenSessionResp();
237130

238-
Long timeToExpire = checkPasswordExpiration(username, password);
131+
long userId = AuthorityChecker.getUserId(username).orElse(-1L);
132+
133+
Long timeToExpire = DataNodeAuthUtils.checkPasswordExpiration(userId, password);
239134
if (timeToExpire != null && timeToExpire <= System.currentTimeMillis()) {
240135
openSessionResp
241136
.sessionId(-1)
@@ -244,7 +139,6 @@ public BasicOpenSessionResp login(
244139
return openSessionResp;
245140
}
246141

247-
long userId = AuthorityChecker.getUserId(username).orElse(-1L);
248142
boolean enableLoginLock = userId != -1;
249143
LoginLockManager loginLockManager = LoginLockManager.getInstance();
250144
if (enableLoginLock && loginLockManager.checkLock(userId, session.getClientAddress())) {
@@ -281,7 +175,7 @@ public BasicOpenSessionResp login(
281175
username);
282176
long currentTime = CommonDateTimeUtils.currentTime();
283177
TSStatus tsStatus =
284-
DataNodeAuthUtils.recordPasswordHistory(username, password, password, currentTime);
178+
DataNodeAuthUtils.recordPasswordHistory(userId, password, password, currentTime);
285179
if (tsStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
286180
openSessionResp
287181
.sessionId(-1)

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1406,7 +1406,7 @@ private void visitUpdateUser(RelationalAuthorStatement node) {
14061406
throw new SemanticException("User " + node.getUserName() + " not found");
14071407
}
14081408
node.setOldPassword(user.getPassword());
1409-
DataNodeAuthUtils.verifyPasswordReuse(node.getUserName(), node.getPassword());
1409+
DataNodeAuthUtils.verifyPasswordReuse(node.getAssociatedUserId(), node.getPassword());
14101410
}
14111411

14121412
@Override

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,8 @@ private void visitUpdateUser(AuthorStatement statement) {
332332
throw new SemanticException("User " + statement.getUserName() + " not found");
333333
}
334334
statement.setPassWord(user.getPassword());
335-
DataNodeAuthUtils.verifyPasswordReuse(statement.getUserName(), statement.getNewPassword());
335+
DataNodeAuthUtils.verifyPasswordReuse(
336+
statement.getAssociatedUsedId(), statement.getNewPassword());
336337
}
337338

338339
private void visitRenameUser(AuthorStatement statement) {

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ public class RelationalAuthorStatement extends Statement {
5757
private String newUsername = "";
5858
private String loginAddr;
5959

60+
// the id of userName
61+
private long associatedUserId = -1;
62+
6063
public RelationalAuthorStatement(
6164
AuthorRType authorType,
6265
String userName,
@@ -162,6 +165,9 @@ public void setDatabase(String database) {
162165

163166
public void setUserName(String userName) {
164167
this.userName = userName;
168+
if (authorType != AuthorRType.CREATE_USER) {
169+
this.associatedUserId = AuthorityChecker.getUserId(userName).orElse(-1L);
170+
}
165171
}
166172

167173
public void setRoleName(String roleName) {
@@ -301,11 +307,12 @@ public TSStatus onSuccess() {
301307
}
302308

303309
private TSStatus onCreateUserSuccess() {
310+
associatedUserId = AuthorityChecker.getUserId(userName).orElse(-1L);
304311
// the old password is expected to be encrypted during updates, so we also encrypt it here to
305312
// keep consistency
306313
TSStatus tsStatus =
307314
DataNodeAuthUtils.recordPasswordHistory(
308-
userName,
315+
associatedUserId,
309316
password,
310317
AuthUtils.encryptPassword(password),
311318
CommonDateTimeUtils.currentTime());
@@ -320,7 +327,7 @@ private TSStatus onCreateUserSuccess() {
320327
private TSStatus onUpdateUserSuccess() {
321328
TSStatus tsStatus =
322329
DataNodeAuthUtils.recordPasswordHistory(
323-
userName, password, oldPassword, CommonDateTimeUtils.currentTime());
330+
associatedUserId, password, oldPassword, CommonDateTimeUtils.currentTime());
324331
try {
325332
RpcUtils.verifySuccess(tsStatus);
326333
} catch (StatementExecutionException e) {
@@ -330,7 +337,7 @@ private TSStatus onUpdateUserSuccess() {
330337
}
331338

332339
private TSStatus onDropUserSuccess() {
333-
TSStatus tsStatus = DataNodeAuthUtils.deletePasswordHistory(userName);
340+
TSStatus tsStatus = DataNodeAuthUtils.deletePasswordHistory(associatedUserId);
334341
try {
335342
RpcUtils.verifySuccess(tsStatus);
336343
} catch (StatementExecutionException e) {
@@ -435,4 +442,8 @@ public TSStatus checkStatementIsValid(String currentUser) {
435442
}
436443
return RpcUtils.SUCCESS_STATUS;
437444
}
445+
446+
public long getAssociatedUserId() {
447+
return associatedUserId;
448+
}
438449
}

0 commit comments

Comments
 (0)