This is an automated email from the ASF dual-hosted git repository.
jt2594838 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 86624a137bb Improve pipe receiver session handling (#18084)
86624a137bb is described below
commit 86624a137bb5448b0d5a5d50b1414ceb5b1bb81a
Author: Caideyipi <[email protected]>
AuthorDate: Tue Jul 7 15:12:18 2026 +0800
Improve pipe receiver session handling (#18084)
* Improve pipe receiver session handling
* Fix pipe receiver test body handling
* Internationalize two-stage receiver login failure
---
.../receiver/protocol/IoTDBConfigNodeReceiver.java | 56 +++++++++++--
.../apache/iotdb/db/i18n/DataNodePipeMessages.java | 2 +
.../apache/iotdb/db/i18n/DataNodePipeMessages.java | 3 +-
.../task/builder/PipeDataNodeTaskBuilder.java | 36 ++++++++
.../agent/task/stage/PipeTaskProcessorStage.java | 5 +-
.../receiver/TwoStageAggregateReceiver.java | 10 +++
.../exchange/sender/TwoStageAggregateSender.java | 75 +++++++++++++----
.../twostage/plugin/TwoStageCountProcessor.java | 9 +-
.../protocol/thrift/IoTDBDataNodeReceiver.java | 62 +++++++++++---
.../protocol/thrift/impl/ClientRPCServiceImpl.java | 42 +++++++++-
.../iotdb/db/pipe/sink/PipeReceiverTest.java | 70 ++++++++++++----
.../env/PipeTaskProcessorRuntimeEnvironment.java | 23 ++++++
.../commons/pipe/receiver/IoTDBFileReceiver.java | 66 +++++++++++----
.../pipe/receiver/IoTDBFileReceiverTest.java | 95 +++++++++++++++++++++-
14 files changed, 484 insertions(+), 70 deletions(-)
diff --git
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
index 5d632bde5bb..eaac8ff8e20 100644
---
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
+++
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java
@@ -22,6 +22,7 @@ package
org.apache.iotdb.confignode.manager.pipe.receiver.protocol;
import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.commons.audit.AuditLogOperation;
import org.apache.iotdb.commons.audit.IAuditEntity;
+import org.apache.iotdb.commons.audit.UserEntity;
import org.apache.iotdb.commons.auth.entity.PrivilegeType;
import org.apache.iotdb.commons.auth.entity.PrivilegeUnion;
import org.apache.iotdb.commons.conf.CommonDescriptor;
@@ -95,7 +96,6 @@ import
org.apache.iotdb.confignode.manager.pipe.event.PipeConfigRegionSnapshotEv
import
org.apache.iotdb.confignode.manager.pipe.metric.receiver.PipeConfigNodeReceiverMetrics;
import
org.apache.iotdb.confignode.manager.pipe.receiver.visitor.PipeConfigPhysicalPlanExceptionVisitor;
import
org.apache.iotdb.confignode.manager.pipe.receiver.visitor.PipeConfigPhysicalPlanTSStatusVisitor;
-import
org.apache.iotdb.confignode.manager.pipe.sink.payload.PipeTransferConfigNodeHandshakeV1Req;
import
org.apache.iotdb.confignode.manager.pipe.sink.payload.PipeTransferConfigNodeHandshakeV2Req;
import
org.apache.iotdb.confignode.manager.pipe.sink.payload.PipeTransferConfigPlanReq;
import
org.apache.iotdb.confignode.manager.pipe.sink.payload.PipeTransferConfigSnapshotPieceReq;
@@ -186,13 +186,15 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
ManagerMessages
.MESSAGE_RECEIVER_CONFIGNODE_HAS_SET_UP_NEW_RECEIVER_SENDER_MUST_RE_77B80C51));
}
+ final TPipeTransferResp authResp =
checkPipeTransferAuthenticated(type);
+ if (Objects.nonNull(authResp)) {
+ return authResp;
+ }
final TPipeTransferResp resp;
final long startTime = System.nanoTime();
switch (type) {
case HANDSHAKE_CONFIGNODE_V1:
- resp =
- handleTransferHandshakeV1(
-
PipeTransferConfigNodeHandshakeV1Req.fromTPipeTransferReq(req));
+ resp = new TPipeTransferResp(getUnsupportedHandshakeV1Status());
PipeConfigNodeReceiverMetrics.getInstance()
.recordHandshakeConfigNodeV1Timer(System.nanoTime() -
startTime);
return resp;
@@ -200,7 +202,10 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
resp =
handleTransferHandshakeV2(
PipeTransferConfigNodeHandshakeV2Req.fromTPipeTransferReq(req));
- userEntity.setAuditLogOperation(AuditLogOperation.DDL);
+ if (resp.getStatus().getCode() ==
TSStatusCode.SUCCESS_STATUS.getStatusCode()
+ && Objects.nonNull(userEntity)) {
+ userEntity.setAuditLogOperation(AuditLogOperation.DDL);
+ }
PipeConfigNodeReceiverMetrics.getInstance()
.recordHandshakeConfigNodeV2Timer(System.nanoTime() -
startTime);
return resp;
@@ -263,6 +268,37 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
&& type != PipeRequestType.HANDSHAKE_CONFIGNODE_V2;
}
+ private TPipeTransferResp checkPipeTransferAuthenticated(final
PipeRequestType type) {
+ if (!requiresAuthentication(type)) {
+ return null;
+ }
+
+ final IClientSession clientSession = SESSION_MANAGER.getCurrSession();
+ if (hasPipeHandshakeCredential || (clientSession != null &&
clientSession.isLogin())) {
+ if (!hasPipeHandshakeCredential && clientSession != null) {
+ username = clientSession.getUsername();
+ userEntity =
+ new UserEntity(clientSession.getUserId(), username,
clientSession.getClientAddress())
+ .setAuditLogOperation(AuditLogOperation.DDL);
+ }
+ return null;
+ }
+
+ return new TPipeTransferResp(getNotLoggedInStatus());
+ }
+
+ private static boolean requiresAuthentication(final PipeRequestType type) {
+ switch (type) {
+ case TRANSFER_CONFIG_PLAN:
+ case TRANSFER_CONFIG_SNAPSHOT_PIECE:
+ case TRANSFER_CONFIG_SNAPSHOT_SEAL:
+ case TRANSFER_COMPRESSED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
private TPipeTransferResp handleTransferConfigPlan(final
PipeTransferConfigPlanReq req)
throws IOException {
return new TPipeTransferResp(
@@ -1222,11 +1258,19 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
// 2. The detection period (300s) is too long for configPlans.
@Override
protected boolean shouldLogin() {
- return true;
+ final IClientSession clientSession = SESSION_MANAGER.getCurrSession();
+ return hasPipeHandshakeCredential || clientSession == null ||
!clientSession.isLogin();
}
@Override
protected TSStatus login() {
+ final IClientSession session = SESSION_MANAGER.getCurrSession();
+ if (!hasPipeHandshakeCredential) {
+ return session != null && session.isLogin()
+ ? RpcUtils.SUCCESS_STATUS
+ : getNotLoggedInStatus();
+ }
+
return configManager.login(username, password, false).getStatus();
}
diff --git
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
index 2f3fdd5ff64..3649c0586b9 100644
---
a/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
+++
b/iotdb-core/datanode/src/main/i18n/en/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
@@ -401,6 +401,8 @@ public final class DataNodePipeMessages {
"Error occurred when closing CombineHandler(id = {})";
public static final String ERROR_OCCURS_WHEN_RECEIVING_REQUEST =
"Error occurs when receiving request: {}.";
+ public static final String LOGIN_FAILED_OR_SESSION_TIMED_OUT =
+ "Log in failed. Either you are not authorized or the session has timed
out.";
public static final String FAILED_TO_CLOSE_IOTDBSYNCCLIENT = "Failed to
close IoTDBSyncClient";
public static final String FAILED_TO_CLOSE_OLD_IOTDBSYNCCLIENT =
"Failed to close old IoTDBSyncClient";
diff --git
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
index b2b67f4d4b5..b83de094ae2 100644
---
a/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
+++
b/iotdb-core/datanode/src/main/i18n/zh/org/apache/iotdb/db/i18n/DataNodePipeMessages.java
@@ -384,7 +384,8 @@ public final class DataNodePipeMessages {
"两阶段聚合 pipe(pipeName={}, creationTime={})的 endpoints 已更新为 {}";
public static final String ERROR_OCCURRED_WHEN_CLOSING_COMBINEHANDLER_ID =
"关闭 CombineHandler(id = {})时发生错误";
- public static final String ERROR_OCCURS_WHEN_RECEIVING_REQUEST =
"接收请求时发生错误:{}";
+ public static final String ERROR_OCCURS_WHEN_RECEIVING_REQUEST =
"接收请求时发生错误:{}。";
+ public static final String LOGIN_FAILED_OR_SESSION_TIMED_OUT =
"登录失败。您未被授权或会话已超时。";
public static final String FAILED_TO_CLOSE_IOTDBSYNCCLIENT = "关闭
IoTDBSyncClient 失败";
public static final String FAILED_TO_CLOSE_OLD_IOTDBSYNCCLIENT = "关闭 old
IoTDBSyncClient 失败";
public static final String FAILED_TO_COMBINE_COUNT = "合并 count 失败:";
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/builder/PipeDataNodeTaskBuilder.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/builder/PipeDataNodeTaskBuilder.java
index 878823d4696..98157a2a49a 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/builder/PipeDataNodeTaskBuilder.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/builder/PipeDataNodeTaskBuilder.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.pipe.agent.task.builder;
+import org.apache.iotdb.commons.audit.UserEntity;
import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex;
import org.apache.iotdb.commons.exception.IllegalPathException;
import org.apache.iotdb.commons.pipe.agent.plugin.builtin.BuiltinPipePlugin;
@@ -47,6 +48,7 @@ import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
import static
org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant.CONNECTOR_FORMAT_HYBRID_VALUE;
import static
org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant.CONNECTOR_FORMAT_KEY;
@@ -127,6 +129,8 @@ public class PipeDataNodeTaskBuilder {
sinkStage.getPipeSinkPendingQueue(),
PROCESSOR_EXECUTOR,
pipeTaskMeta,
+ getSourceUserEntity(sourceParameters),
+ getSourcePassword(sourceParameters),
pipeStaticMeta
.getSinkParameters()
.getStringOrDefault(
@@ -139,6 +143,38 @@ public class PipeDataNodeTaskBuilder {
pipeStaticMeta.getPipeName(), regionId, sourceStage, processorStage,
sinkStage);
}
+ private UserEntity getSourceUserEntity(final PipeParameters
sourceParameters) {
+ final String username =
+ sourceParameters.getStringByKeys(
+ PipeSourceConstant.EXTRACTOR_IOTDB_USER_KEY,
+ PipeSourceConstant.SOURCE_IOTDB_USER_KEY,
+ PipeSourceConstant.EXTRACTOR_IOTDB_USERNAME_KEY,
+ PipeSourceConstant.SOURCE_IOTDB_USERNAME_KEY);
+ if (Objects.isNull(username)) {
+ return null;
+ }
+
+ final String userId =
+ sourceParameters.getStringOrDefault(
+ Arrays.asList(
+ PipeSourceConstant.EXTRACTOR_IOTDB_USER_ID,
+ PipeSourceConstant.SOURCE_IOTDB_USER_ID),
+ "-1");
+ final String cliHostname =
+ sourceParameters.getStringOrDefault(
+ Arrays.asList(
+ PipeSourceConstant.EXTRACTOR_IOTDB_CLI_HOSTNAME,
+ PipeSourceConstant.SOURCE_IOTDB_CLI_HOSTNAME),
+ "");
+ return new UserEntity(Long.parseLong(userId), username, cliHostname);
+ }
+
+ private String getSourcePassword(final PipeParameters sourceParameters) {
+ return sourceParameters.getStringByKeys(
+ PipeSourceConstant.EXTRACTOR_IOTDB_PASSWORD_KEY,
+ PipeSourceConstant.SOURCE_IOTDB_PASSWORD_KEY);
+ }
+
public static PipeParameters blendUserAndSystemParameters(
final PipeParameters userParameters, final PipeTaskMeta pipeTaskMeta) {
// Deep copy the user parameters to avoid modification of the original
parameters.
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/stage/PipeTaskProcessorStage.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/stage/PipeTaskProcessorStage.java
index 2373495c8eb..85a3ff4cd3c 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/stage/PipeTaskProcessorStage.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/stage/PipeTaskProcessorStage.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.pipe.agent.task.stage;
+import org.apache.iotdb.commons.audit.UserEntity;
import org.apache.iotdb.commons.consensus.DataRegionId;
import org.apache.iotdb.commons.pipe.agent.plugin.builtin.BuiltinPipePlugin;
import org.apache.iotdb.commons.pipe.agent.task.connection.EventSupplier;
@@ -70,12 +71,14 @@ public class PipeTaskProcessorStage extends PipeTaskStage {
final UnboundedBlockingPendingQueue<Event> pipeSinkOutputPendingQueue,
final PipeProcessorSubtaskExecutor executor,
final PipeTaskMeta pipeTaskMeta,
+ final UserEntity sourceUserEntity,
+ final String sourcePassword,
final boolean forceTabletFormat,
final boolean skipParsing) {
final PipeProcessorRuntimeConfiguration runtimeConfiguration =
new PipeTaskRuntimeConfiguration(
new PipeTaskProcessorRuntimeEnvironment(
- pipeName, creationTime, regionId, pipeTaskMeta));
+ pipeName, creationTime, regionId, pipeTaskMeta,
sourceUserEntity, sourcePassword));
final PipeProcessor pipeProcessor =
StorageEngine.getInstance().getAllDataRegionIds().contains(new
DataRegionId(regionId))
|| PipeRuntimeMeta.isSourceExternal(regionId)
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/exchange/receiver/TwoStageAggregateReceiver.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/exchange/receiver/TwoStageAggregateReceiver.java
index 4ab24850cf9..7484eac6f01 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/exchange/receiver/TwoStageAggregateReceiver.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/exchange/receiver/TwoStageAggregateReceiver.java
@@ -26,6 +26,8 @@ import
org.apache.iotdb.db.pipe.processor.twostage.combiner.PipeCombineHandlerMa
import
org.apache.iotdb.db.pipe.processor.twostage.exchange.payload.CombineRequest;
import
org.apache.iotdb.db.pipe.processor.twostage.exchange.payload.FetchCombineResultRequest;
import
org.apache.iotdb.db.pipe.processor.twostage.exchange.payload.RequestType;
+import org.apache.iotdb.db.protocol.session.IClientSession;
+import org.apache.iotdb.db.protocol.session.SessionManager;
import org.apache.iotdb.rpc.RpcUtils;
import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq;
@@ -37,6 +39,7 @@ import org.slf4j.LoggerFactory;
public class TwoStageAggregateReceiver implements IoTDBReceiver {
private static final Logger LOGGER =
LoggerFactory.getLogger(TwoStageAggregateReceiver.class);
+ private static final SessionManager SESSION_MANAGER =
SessionManager.getInstance();
@Override
public IoTDBSinkRequestVersion getVersion() {
@@ -46,6 +49,13 @@ public class TwoStageAggregateReceiver implements
IoTDBReceiver {
@Override
public TPipeTransferResp receive(TPipeTransferReq req) {
try {
+ final IClientSession clientSession = SESSION_MANAGER.getCurrSession();
+ if (!SESSION_MANAGER.checkLogin(clientSession)) {
+ return new TPipeTransferResp(
+ RpcUtils.getStatus(
+ TSStatusCode.NOT_LOGIN,
DataNodePipeMessages.LOGIN_FAILED_OR_SESSION_TIMED_OUT));
+ }
+
final short rawRequestType = req.getType();
if (RequestType.isValidatedRequestType(rawRequestType)) {
switch (RequestType.valueOf(rawRequestType)) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/exchange/sender/TwoStageAggregateSender.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/exchange/sender/TwoStageAggregateSender.java
index e221b4a19c7..f9624086d83 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/exchange/sender/TwoStageAggregateSender.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/exchange/sender/TwoStageAggregateSender.java
@@ -20,8 +20,10 @@
package org.apache.iotdb.db.pipe.processor.twostage.exchange.sender;
import org.apache.iotdb.common.rpc.thrift.TEndPoint;
+import org.apache.iotdb.commons.audit.UserEntity;
import org.apache.iotdb.commons.client.exception.ClientManagerException;
import org.apache.iotdb.commons.client.property.ThriftClientProperty;
+import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.commons.pipe.config.PipeConfig;
import org.apache.iotdb.commons.pipe.sink.client.IoTDBSyncClient;
import org.apache.iotdb.confignode.rpc.thrift.TDataNodeInfo;
@@ -32,14 +34,18 @@ import org.apache.iotdb.db.protocol.client.ConfigNodeClient;
import org.apache.iotdb.db.protocol.client.ConfigNodeClientManager;
import org.apache.iotdb.db.protocol.client.ConfigNodeInfo;
import org.apache.iotdb.pipe.api.exception.PipeException;
+import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq;
import org.apache.iotdb.service.rpc.thrift.TPipeTransferResp;
+import org.apache.iotdb.service.rpc.thrift.TSOpenSessionReq;
+import org.apache.iotdb.service.rpc.thrift.TSOpenSessionResp;
+import org.apache.iotdb.service.rpc.thrift.TSProtocolVersion;
import org.apache.thrift.TException;
-import org.apache.thrift.transport.TTransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.time.ZoneId;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -55,8 +61,12 @@ public class TwoStageAggregateSender implements
AutoCloseable {
private static final PipeConfig PIPE_CONFIG = PipeConfig.getInstance();
+ private static final String USE_ENCRYPTED_PASSWORD_KEY =
"use_encrypted_password";
+
private final String pipeName;
private final long creationTime;
+ private final UserEntity sourceUserEntity;
+ private final String sourcePassword;
private static final AtomicLong DATANODE_ID_2_END_POINTS_LAST_UPDATE_TIME =
new AtomicLong(0);
private static final AtomicReference<Map<Integer, TEndPoint>>
DATANODE_ID_2_END_POINTS =
@@ -67,8 +77,15 @@ public class TwoStageAggregateSender implements
AutoCloseable {
new ConcurrentHashMap<>();
public TwoStageAggregateSender(String pipeName, long creationTime) {
+ this(pipeName, creationTime, null, null);
+ }
+
+ public TwoStageAggregateSender(
+ String pipeName, long creationTime, UserEntity sourceUserEntity, String
sourcePassword) {
this.pipeName = pipeName;
this.creationTime = creationTime;
+ this.sourceUserEntity = sourceUserEntity;
+ this.sourcePassword = sourcePassword;
}
public synchronized TPipeTransferResp request(long watermark,
TPipeTransferReq req)
@@ -178,7 +195,7 @@ public class TwoStageAggregateSender implements
AutoCloseable {
try {
endPointIoTDBSyncClientMap.put(endPoint,
constructIoTDBSyncClient(endPoint));
- } catch (TTransportException e) {
+ } catch (TException e) {
LOGGER.warn(DataNodePipeMessages.FAILED_TO_CONSTRUCT_IOTDBSYNCCLIENT,
e);
}
}
@@ -194,8 +211,7 @@ public class TwoStageAggregateSender implements
AutoCloseable {
}
}
- private IoTDBSyncClient reconstructIoTDBSyncClient(TEndPoint endPoint)
- throws TTransportException {
+ private IoTDBSyncClient reconstructIoTDBSyncClient(TEndPoint endPoint)
throws TException {
final IoTDBSyncClient oldClient =
endPointIoTDBSyncClientMap.remove(endPoint);
if (oldClient != null) {
try {
@@ -209,17 +225,46 @@ public class TwoStageAggregateSender implements
AutoCloseable {
return newClient;
}
- private IoTDBSyncClient constructIoTDBSyncClient(TEndPoint endPoint) throws
TTransportException {
- return new IoTDBSyncClient(
- new ThriftClientProperty.Builder()
-
.setConnectionTimeoutMs(PIPE_CONFIG.getPipeSinkHandshakeTimeoutMs())
-
.setRpcThriftCompressionEnabled(PIPE_CONFIG.isPipeSinkRPCThriftCompressionEnabled())
- .build(),
- endPoint.getIp(),
- endPoint.getPort(),
- false,
- null,
- null);
+ private IoTDBSyncClient constructIoTDBSyncClient(TEndPoint endPoint) throws
TException {
+ final IoTDBSyncClient client =
+ new IoTDBSyncClient(
+ new ThriftClientProperty.Builder()
+
.setConnectionTimeoutMs(PIPE_CONFIG.getPipeSinkHandshakeTimeoutMs())
+
.setRpcThriftCompressionEnabled(PIPE_CONFIG.isPipeSinkRPCThriftCompressionEnabled())
+ .build(),
+ endPoint.getIp(),
+ endPoint.getPort(),
+ false,
+ null,
+ null);
+ openSession(client);
+ return client;
+ }
+
+ private void openSession(final IoTDBSyncClient client) throws TException {
+ if (Objects.isNull(sourceUserEntity) || Objects.isNull(sourcePassword)) {
+ throw new PipeException(
+ String.format(
+ "Missing source credentials for two-stage aggregate pipe %s-%s.",
+ pipeName, creationTime));
+ }
+
+ final TSOpenSessionReq openSessionReq = new TSOpenSessionReq();
+ openSessionReq.setUsername(sourceUserEntity.getUsername());
+ openSessionReq.setPassword(sourcePassword);
+ openSessionReq.setZoneId(ZoneId.systemDefault().toString());
+
openSessionReq.setClient_protocol(TSProtocolVersion.IOTDB_SERVICE_PROTOCOL_V3);
+ openSessionReq.putToConfiguration("version",
IoTDBConstant.ClientVersion.V_1_0.toString());
+ openSessionReq.putToConfiguration(USE_ENCRYPTED_PASSWORD_KEY,
Boolean.TRUE.toString());
+
+ final TSOpenSessionResp openSessionResp =
client.openSession(openSessionReq);
+ if (openSessionResp.getStatus().getCode() !=
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+ throw new PipeException(
+ String.format(
+ "Failed to login for two-stage aggregate pipe %s-%s, status:
%s.",
+ pipeName, creationTime, openSessionResp.getStatus()));
+ }
+ client.setTimeout(PIPE_CONFIG.getPipeSinkTransferTimeoutMs());
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/plugin/TwoStageCountProcessor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/plugin/TwoStageCountProcessor.java
index 22f7c0c1a93..447cf0bbaf5 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/plugin/TwoStageCountProcessor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/processor/twostage/plugin/TwoStageCountProcessor.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.pipe.processor.twostage.plugin;
+import org.apache.iotdb.commons.audit.UserEntity;
import org.apache.iotdb.commons.consensus.DataRegionId;
import org.apache.iotdb.commons.consensus.index.ProgressIndex;
import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex;
@@ -102,6 +103,9 @@ public class TwoStageCountProcessor implements
PipeProcessor {
private final Queue<Pair<long[], ProgressIndex> /* ([timestamp, local
count], progress index) */>
localCommitQueue = new ConcurrentLinkedQueue<>();
+ private UserEntity sourceUserEntity;
+ private String sourcePassword;
+
private TwoStageAggregateSender twoStageAggregateSender;
private final Queue<Pair<Long, Long> /* (timestamp, global count) */>
globalCountQueue =
new ConcurrentLinkedQueue<>();
@@ -139,6 +143,8 @@ public class TwoStageCountProcessor implements
PipeProcessor {
creationTime = runtimeEnvironment.getCreationTime();
regionId = runtimeEnvironment.getRegionId();
pipeTaskMeta = runtimeEnvironment.getPipeTaskMeta();
+ sourceUserEntity = runtimeEnvironment.getSourceUserEntity();
+ sourcePassword = runtimeEnvironment.getSourcePassword();
dataBaseName =
StorageEngine.getInstance()
.getDataRegion(new DataRegionId(runtimeEnvironment.getRegionId()))
@@ -175,7 +181,8 @@ public class TwoStageCountProcessor implements
PipeProcessor {
PipeCombineHandlerManager.getInstance()
.register(
pipeName, creationTime, (combineId) -> new
CountOperator(combineId, globalCountQueue));
- twoStageAggregateSender = new TwoStageAggregateSender(pipeName,
creationTime);
+ twoStageAggregateSender =
+ new TwoStageAggregateSender(pipeName, creationTime, sourceUserEntity,
sourcePassword);
}
static PartialPath parseOutputSeries(final PipeParameters parameters)
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 f4a4baed694..46c00a095c8 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
@@ -66,7 +66,6 @@ import
org.apache.iotdb.db.pipe.receiver.visitor.PipeTreeStatementDataTypeConver
import
org.apache.iotdb.db.pipe.receiver.visitor.PipeTreeStatementToBatchVisitor;
import org.apache.iotdb.db.pipe.resource.PipeDataNodeResourceManager;
import org.apache.iotdb.db.pipe.resource.memory.PipeMemoryBlock;
-import
org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferDataNodeHandshakeV1Req;
import
org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferDataNodeHandshakeV2Req;
import
org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferPlanNodeReq;
import
org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferSchemaSnapshotPieceReq;
@@ -222,20 +221,15 @@ public class IoTDBDataNodeReceiver extends
IoTDBFileReceiver {
if (requestType != PipeRequestType.TRANSFER_SLICE) {
clearSliceReqHandler();
}
+ final TPipeTransferResp authResp =
checkPipeTransferAuthenticated(requestType);
+ if (Objects.nonNull(authResp)) {
+ return authResp;
+ }
switch (requestType) {
case HANDSHAKE_DATANODE_V1:
{
try {
- if (PipeConfig.getInstance().isPipeEnableMemoryCheck()
- &&
PipeDataNodeResourceManager.memory().getFreeMemorySizeInBytes()
- <
PipeConfig.getInstance().getPipeMinimumReceiverMemory()) {
- return new TPipeTransferResp(
- RpcUtils.getStatus(
- TSStatusCode.PIPE_HANDSHAKE_ERROR.getStatusCode(),
- "The receiver memory is not enough to handle the
handshake request from datanode."));
- }
- return handleTransferHandshakeV1(
-
PipeTransferDataNodeHandshakeV1Req.fromTPipeTransferReq(req));
+ return new
TPipeTransferResp(getUnsupportedHandshakeV1Status());
} finally {
PipeDataNodeReceiverMetrics.getInstance()
.recordHandshakeDatanodeV1Timer(System.nanoTime() -
startTime);
@@ -539,6 +533,46 @@ public class IoTDBDataNodeReceiver extends
IoTDBFileReceiver {
return IoTDBDescriptor.getInstance().getConfig().getClusterId();
}
+ private TPipeTransferResp checkPipeTransferAuthenticated(final
PipeRequestType requestType) {
+ if (!requiresAuthentication(requestType)) {
+ return null;
+ }
+
+ final IClientSession clientSession = SESSION_MANAGER.getCurrSession();
+ if (hasPipeHandshakeCredential || (clientSession != null &&
clientSession.isLogin())) {
+ if (!hasPipeHandshakeCredential && clientSession != null) {
+ username = clientSession.getUsername();
+ userEntity = AuthorityChecker.createIAuditEntity(username,
clientSession);
+ }
+ return null;
+ }
+
+ return new TPipeTransferResp(getNotLoggedInStatus());
+ }
+
+ private static boolean requiresAuthentication(final PipeRequestType
requestType) {
+ switch (requestType) {
+ case TRANSFER_TABLET_INSERT_NODE:
+ case TRANSFER_TABLET_INSERT_NODE_V2:
+ case TRANSFER_TABLET_RAW:
+ case TRANSFER_TABLET_RAW_V2:
+ case TRANSFER_TABLET_BINARY:
+ case TRANSFER_TABLET_BINARY_V2:
+ case TRANSFER_TABLET_BATCH:
+ case TRANSFER_TABLET_BATCH_V2:
+ case TRANSFER_TS_FILE_PIECE:
+ case TRANSFER_TS_FILE_SEAL:
+ case TRANSFER_TS_FILE_PIECE_WITH_MOD:
+ case TRANSFER_TS_FILE_SEAL_WITH_MOD:
+ case TRANSFER_PLAN_NODE:
+ case TRANSFER_SCHEMA_SNAPSHOT_PIECE:
+ case TRANSFER_SCHEMA_SNAPSHOT_SEAL:
+ return true;
+ default:
+ return false;
+ }
+ }
+
@Override
protected boolean shouldLogin() {
// The idle time is updated per request
@@ -1190,6 +1224,12 @@ public class IoTDBDataNodeReceiver extends
IoTDBFileReceiver {
protected TSStatus login() {
final IClientSession session = SESSION_MANAGER.getCurrSession();
+ if (!hasPipeHandshakeCredential) {
+ return session != null && session.isLogin()
+ ? RpcUtils.SUCCESS_STATUS
+ : getNotLoggedInStatus();
+ }
+
if (session != null && !session.isLogin()) {
final BasicOpenSessionResp openSessionResp =
SESSION_MANAGER.login(
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
index b6541a4f0a0..40e4697bb4e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java
@@ -147,6 +147,8 @@ import org.apache.iotdb.rpc.RpcUtils;
import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.rpc.stmt.PreparedParameterSerde;
import org.apache.iotdb.rpc.stmt.PreparedParameterSerde.DeserializedParam;
+import
org.apache.iotdb.rpc.subscription.payload.response.PipeSubscribeResponseType;
+import
org.apache.iotdb.rpc.subscription.payload.response.PipeSubscribeResponseVersion;
import org.apache.iotdb.service.rpc.thrift.ServerProperties;
import
org.apache.iotdb.service.rpc.thrift.TCreateTimeseriesUsingSchemaTemplateReq;
import org.apache.iotdb.service.rpc.thrift.TPipeSubscribeReq;
@@ -3476,17 +3478,51 @@ public class ClientRPCServiceImpl implements
IClientRPCServiceWithHandler {
@Override
public TPipeSubscribeResp pipeSubscribe(final TPipeSubscribeReq req) {
- return SubscriptionAgent.receiver().handle(req);
+ try {
+ final IClientSession clientSession = SESSION_MANAGER.getCurrSession();
+ if (!SESSION_MANAGER.checkLogin(clientSession)) {
+ return getNotLoggedInPipeSubscribeResp();
+ }
+
+ return SubscriptionAgent.receiver().handle(req);
+ } finally {
+ SESSION_MANAGER.updateIdleTime();
+ }
}
@Override
public TSBackupConfigurationResp getBackupConfiguration() {
- return new
TSBackupConfigurationResp(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS));
+ try {
+ final IClientSession clientSession = SESSION_MANAGER.getCurrSession();
+ if (!SESSION_MANAGER.checkLogin(clientSession)) {
+ return new TSBackupConfigurationResp(getNotLoggedInStatus());
+ }
+
+ return new
TSBackupConfigurationResp(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS));
+ } finally {
+ SESSION_MANAGER.updateIdleTime();
+ }
}
@Override
public TSConnectionInfoResp fetchAllConnectionsInfo() {
- return SESSION_MANAGER.getAllConnectionInfo();
+ try {
+ final IClientSession clientSession = SESSION_MANAGER.getCurrSession();
+ if (!SESSION_MANAGER.checkLogin(clientSession)) {
+ return new TSConnectionInfoResp(Collections.emptyList());
+ }
+
+ return SESSION_MANAGER.getAllConnectionInfo();
+ } finally {
+ SESSION_MANAGER.updateIdleTime();
+ }
+ }
+
+ private TPipeSubscribeResp getNotLoggedInPipeSubscribeResp() {
+ return new TPipeSubscribeResp(
+ getNotLoggedInStatus(),
+ PipeSubscribeResponseVersion.VERSION_1.getVersion(),
+ PipeSubscribeResponseType.ACK.getType());
}
@Override
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/sink/PipeReceiverTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/sink/PipeReceiverTest.java
index 5b38d7bfdfd..a6d4011505f 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/sink/PipeReceiverTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/sink/PipeReceiverTest.java
@@ -20,35 +20,77 @@
package org.apache.iotdb.db.pipe.sink;
import org.apache.iotdb.commons.conf.CommonDescriptor;
+import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.IoTDBSinkRequestVersion;
+import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeRequestType;
+import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferCompressedReq;
+import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferSliceReq;
import org.apache.iotdb.db.pipe.receiver.protocol.thrift.IoTDBDataNodeReceiver;
import
org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferDataNodeHandshakeV1Req;
-import
org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferTabletRawReq;
+import org.apache.iotdb.rpc.TSStatusCode;
+import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq;
+import org.apache.iotdb.service.rpc.thrift.TPipeTransferResp;
-import org.apache.tsfile.enums.TSDataType;
-import org.apache.tsfile.write.record.Tablet;
-import org.apache.tsfile.write.schema.MeasurementSchema;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.Collections;
public class PipeReceiverTest {
@Test
- public void testIoTDBThriftReceiverV1() {
+ public void testUnauthenticatedPipeTransferRejected() {
+ final IoTDBDataNodeReceiver receiver = new IoTDBDataNodeReceiver();
+
+ final TPipeTransferResp resp =
receiver.receive(buildEmptyRawTabletTransferReq());
+
+ Assert.assertEquals(TSStatusCode.NOT_LOGIN.getStatusCode(),
resp.getStatus().getCode());
+ }
+
+ @Test
+ public void testUnauthenticatedWrappedPipeTransferRejected() throws
IOException {
+ final IoTDBDataNodeReceiver receiver = new IoTDBDataNodeReceiver();
+ final TPipeTransferReq rawReq = buildEmptyRawTabletTransferReq();
+
+ final TPipeTransferResp compressedResp =
+ receiver.receive(
+ PipeTransferCompressedReq.toTPipeTransferReq(rawReq,
Collections.emptyList()));
+ Assert.assertEquals(
+ TSStatusCode.NOT_LOGIN.getStatusCode(),
compressedResp.getStatus().getCode());
+
+ final TPipeTransferReq sliceReq =
+ PipeTransferSliceReq.toTPipeTransferReq(
+ 0,
+ PipeRequestType.TRANSFER_TABLET_RAW.getType(),
+ 0,
+ 1,
+ rawReq.body.duplicate(),
+ 0,
+ rawReq.body.limit());
+ final TPipeTransferResp sliceResp = receiver.receive(sliceReq);
+ Assert.assertEquals(TSStatusCode.NOT_LOGIN.getStatusCode(),
sliceResp.getStatus().getCode());
+ }
+
+ @Test
+ public void testIoTDBThriftReceiverV1HandshakeRejected() {
IoTDBDataNodeReceiver receiver = new IoTDBDataNodeReceiver();
try {
- receiver.receive(
- PipeTransferDataNodeHandshakeV1Req.toTPipeTransferReq(
-
CommonDescriptor.getInstance().getConfig().getTimestampPrecision()));
- receiver.receive(
- PipeTransferTabletRawReq.toTPipeTransferReq(
- new Tablet(
- "root.sg.d",
- Collections.singletonList(new MeasurementSchema("s",
TSDataType.INT32))),
- true));
+ final TPipeTransferResp handshakeResp =
+ receiver.receive(
+ PipeTransferDataNodeHandshakeV1Req.toTPipeTransferReq(
+
CommonDescriptor.getInstance().getConfig().getTimestampPrecision()));
+ Assert.assertEquals(
+ TSStatusCode.PIPE_HANDSHAKE_ERROR.getStatusCode(),
handshakeResp.getStatus().getCode());
} catch (IOException e) {
Assert.fail();
}
}
+
+ private TPipeTransferReq buildEmptyRawTabletTransferReq() {
+ final TPipeTransferReq req = new TPipeTransferReq();
+ req.setVersion(IoTDBSinkRequestVersion.VERSION_1.getVersion());
+ req.setType(PipeRequestType.TRANSFER_TABLET_RAW.getType());
+ req.setBody(ByteBuffer.allocate(0));
+ return req;
+ }
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/config/plugin/env/PipeTaskProcessorRuntimeEnvironment.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/config/plugin/env/PipeTaskProcessorRuntimeEnvironment.java
index 6e5cf887938..11111b0d9f5 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/config/plugin/env/PipeTaskProcessorRuntimeEnvironment.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/config/plugin/env/PipeTaskProcessorRuntimeEnvironment.java
@@ -19,19 +19,42 @@
package org.apache.iotdb.commons.pipe.config.plugin.env;
+import org.apache.iotdb.commons.audit.UserEntity;
import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta;
public class PipeTaskProcessorRuntimeEnvironment extends
PipeTaskRuntimeEnvironment {
private final PipeTaskMeta pipeTaskMeta;
+ private final UserEntity sourceUserEntity;
+ private final String sourcePassword;
public PipeTaskProcessorRuntimeEnvironment(
String pipeName, long creationTime, int regionId, PipeTaskMeta
pipeTaskMeta) {
+ this(pipeName, creationTime, regionId, pipeTaskMeta, null, null);
+ }
+
+ public PipeTaskProcessorRuntimeEnvironment(
+ String pipeName,
+ long creationTime,
+ int regionId,
+ PipeTaskMeta pipeTaskMeta,
+ UserEntity sourceUserEntity,
+ String sourcePassword) {
super(pipeName, creationTime, regionId);
this.pipeTaskMeta = pipeTaskMeta;
+ this.sourceUserEntity = sourceUserEntity;
+ this.sourcePassword = sourcePassword;
}
public PipeTaskMeta getPipeTaskMeta() {
return pipeTaskMeta;
}
+
+ public UserEntity getSourceUserEntity() {
+ return sourceUserEntity;
+ }
+
+ public String getSourcePassword() {
+ return sourcePassword;
+ }
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/receiver/IoTDBFileReceiver.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/receiver/IoTDBFileReceiver.java
index f05e5069eed..c39b0d93c6f 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/receiver/IoTDBFileReceiver.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/receiver/IoTDBFileReceiver.java
@@ -83,6 +83,7 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
protected String username = CONNECTOR_IOTDB_USER_DEFAULT_VALUE;
protected String password = CONNECTOR_IOTDB_PASSWORD_DEFAULT_VALUE;
protected IAuditEntity userEntity;
+ protected boolean hasPipeHandshakeCredential = false;
protected long lastSuccessfulLoginTime = Long.MIN_VALUE;
@@ -107,6 +108,8 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
}
protected TPipeTransferResp handleTransferHandshakeV1(final
PipeTransferHandshakeV1Req req) {
+ hasPipeHandshakeCredential = false;
+
if (!CommonDescriptor.getInstance()
.getConfig()
.getTimestampPrecision()
@@ -227,6 +230,8 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
protected TPipeTransferResp handleTransferHandshakeV2(final
PipeTransferHandshakeV2Req req)
throws IOException {
+ hasPipeHandshakeCredential = false;
+
// Reject to handshake if the receiver can not take clusterId from config
node.
final String clusterIdFromConfigNode = getClusterId();
if (clusterIdFromConfigNode == null) {
@@ -281,30 +286,41 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
if (userIdString != null) {
userId = Long.parseLong(userIdString);
}
- final String usernameString =
-
req.getParams().get(PipeTransferHandshakeConstant.HANDSHAKE_KEY_USERNAME);
- if (usernameString != null) {
- username = usernameString;
- }
final String cliHostnameString =
req.getParams().get(PipeTransferHandshakeConstant.HANDSHAKE_KEY_CLI_HOSTNAME);
if (cliHostnameString != null) {
cliHostname = cliHostnameString;
}
- userEntity = new UserEntity(userId, username, cliHostname);
-
+ final String usernameString =
+
req.getParams().get(PipeTransferHandshakeConstant.HANDSHAKE_KEY_USERNAME);
final String passwordString =
req.getParams().get(PipeTransferHandshakeConstant.HANDSHAKE_KEY_PASSWORD);
- if (passwordString != null) {
- password = passwordString;
+ if (usernameString == null || passwordString == null) {
+ return new TPipeTransferResp(
+ RpcUtils.getStatus(
+ TSStatusCode.NOT_LOGIN, "Pipe handshake missing username or
password."));
+ }
+
+ username = usernameString;
+ password = passwordString;
+ userEntity = new UserEntity(userId, username, cliHostname);
+ hasPipeHandshakeCredential = true;
+
+ final TSStatus status;
+ try {
+ status = login();
+ } catch (final Exception e) {
+ hasPipeHandshakeCredential = false;
+ throw e;
}
- final TSStatus status = loginIfNecessary();
if (status.code != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+ hasPipeHandshakeCredential = false;
PipeLogger.log(
LOGGER::warn, PipeMessages.RECEIVER_HANDSHAKE_FAILED_LOGIN,
receiverId.get(), status);
return new TPipeTransferResp(status);
} else {
+ lastSuccessfulLoginTime = System.currentTimeMillis();
LOGGER.info(PipeMessages.RECEIVER_USER_LOGIN_SUCCESS, receiverId.get(),
username);
}
@@ -344,13 +360,17 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
// Handle the handshake request as a v1 request.
// Here we construct a fake "dataNode" request to valid from v1 validation
logic, though
// it may not require the actual type of the v1 request.
- return handleTransferHandshakeV1(
- new PipeTransferHandshakeV1Req() {
- @Override
- protected PipeRequestType getPlanType() {
- return PipeRequestType.HANDSHAKE_DATANODE_V1;
- }
- }.convertToTPipeTransferReq(timestampPrecision));
+ final TPipeTransferResp handshakeResp =
+ handleTransferHandshakeV1(
+ new PipeTransferHandshakeV1Req() {
+ @Override
+ protected PipeRequestType getPlanType() {
+ return PipeRequestType.HANDSHAKE_DATANODE_V1;
+ }
+ }.convertToTPipeTransferReq(timestampPrecision));
+ hasPipeHandshakeCredential =
+ handshakeResp.getStatus().getCode() ==
TSStatusCode.SUCCESS_STATUS.getStatusCode();
+ return handshakeResp;
}
protected abstract String getClusterId();
@@ -381,6 +401,18 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
return StatusUtils.OK;
}
+ protected TSStatus getNotLoggedInStatus() {
+ return RpcUtils.getStatus(
+ TSStatusCode.NOT_LOGIN,
+ "Log in failed. Either you are not authorized or the session has timed
out.");
+ }
+
+ protected TSStatus getUnsupportedHandshakeV1Status() {
+ return RpcUtils.getStatus(
+ TSStatusCode.PIPE_HANDSHAKE_ERROR,
+ "Pipe handshake V1 is no longer supported. Please use handshake V2
with username and password.");
+ }
+
protected abstract TSStatus login();
protected final TPipeTransferResp handleTransferFilePiece(
diff --git
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/receiver/IoTDBFileReceiverTest.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/receiver/IoTDBFileReceiverTest.java
index f774d725bae..9cf7a9f233b 100644
---
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/receiver/IoTDBFileReceiverTest.java
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/receiver/IoTDBFileReceiverTest.java
@@ -23,11 +23,13 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.commons.conf.CommonDescriptor;
import org.apache.iotdb.commons.exception.IllegalPathException;
import
org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException;
+import
org.apache.iotdb.commons.pipe.sink.payload.thrift.common.PipeTransferHandshakeConstant;
import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeRequestType;
import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferFilePieceReq;
import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferFileSealReqV1;
import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferFileSealReqV2;
import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferHandshakeV1Req;
+import
org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferHandshakeV2Req;
import
org.apache.iotdb.commons.pipe.sink.payload.thrift.response.PipeTransferFilePieceResp;
import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq;
@@ -44,7 +46,9 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class IoTDBFileReceiverTest {
@@ -118,6 +122,64 @@ public class IoTDBFileReceiverTest {
}
}
+ @Test
+ public void testHandshakeV1ClearsPipeCredential() throws Exception {
+ final Path baseDir = Files.createTempDirectory("iotdb-file-receiver-test");
+ final DummyFileReceiver receiver = new DummyFileReceiver(baseDir.toFile());
+ try {
+ receiver.setHasPipeHandshakeCredential(true);
+
+ receiver.handshake();
+
+ Assert.assertFalse(receiver.hasPipeHandshakeCredential());
+ } finally {
+ receiver.handleExit();
+ }
+ }
+
+ @Test
+ public void testHandshakeV2RequiresCredentials() throws Exception {
+ final Path baseDir = Files.createTempDirectory("iotdb-file-receiver-test");
+ final DummyFileReceiver receiver = new DummyFileReceiver(baseDir.toFile());
+ try {
+ final TPipeTransferResp response =
receiver.handshakeV2(buildHandshakeV2Params(false));
+
+ Assert.assertEquals(TSStatusCode.NOT_LOGIN.getStatusCode(),
response.getStatus().getCode());
+ Assert.assertEquals(0, receiver.getLoginCallCount());
+ } finally {
+ receiver.handleExit();
+ }
+ }
+
+ @Test
+ public void testHandshakeV2AuthenticatesImmediately() throws Exception {
+ final Path baseDir = Files.createTempDirectory("iotdb-file-receiver-test");
+ final DummyFileReceiver receiver = new DummyFileReceiver(baseDir.toFile());
+ try {
+ final TPipeTransferResp response =
receiver.handshakeV2(buildHandshakeV2Params(true));
+
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
response.getStatus().getCode());
+ Assert.assertEquals(1, receiver.getLoginCallCount());
+ Assert.assertTrue(receiver.hasPipeHandshakeCredential());
+ } finally {
+ receiver.handleExit();
+ }
+ }
+
+ private Map<String, String> buildHandshakeV2Params(final boolean
includeCredentials) {
+ final Map<String, String> params = new HashMap<>();
+ params.put(PipeTransferHandshakeConstant.HANDSHAKE_KEY_CLUSTER_ID,
"sender-cluster");
+ params.put(
+ PipeTransferHandshakeConstant.HANDSHAKE_KEY_TIME_PRECISION,
+ CommonDescriptor.getInstance().getConfig().getTimestampPrecision());
+ if (includeCredentials) {
+ params.put(PipeTransferHandshakeConstant.HANDSHAKE_KEY_USERNAME, "root");
+ params.put(PipeTransferHandshakeConstant.HANDSHAKE_KEY_PASSWORD, "root");
+ }
+ return params;
+ }
+
@Test
public void testSealFileV1FailureDeletesTransferredFile() throws Exception {
final Path baseDir = Files.createTempDirectory("iotdb-file-receiver-test");
@@ -188,6 +250,7 @@ public class IoTDBFileReceiverTest {
private final File receiverFileBaseDir;
private TSStatus loadFileV1Status = new
TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
+ private int loginCallCount = 0;
private boolean failFilePieceMemoryAllocation = false;
private int filePieceMemoryCloseCount = 0;
@@ -206,6 +269,10 @@ public class IoTDBFileReceiverTest {
CommonDescriptor.getInstance().getConfig().getTimestampPrecision()));
}
+ TPipeTransferResp handshakeV2(final Map<String, String> params) throws
IOException {
+ return
handleTransferHandshakeV2(DummyHandshakeV2Req.toTPipeTransferReq(params));
+ }
+
void writeToCurrentWritingFile(final byte[] bytes) throws Exception {
getCurrentWritingFileWriter().write(bytes);
}
@@ -214,6 +281,18 @@ public class IoTDBFileReceiverTest {
loadFileV1Status = status;
}
+ void setHasPipeHandshakeCredential(final boolean
hasPipeHandshakeCredential) {
+ this.hasPipeHandshakeCredential = hasPipeHandshakeCredential;
+ }
+
+ boolean hasPipeHandshakeCredential() {
+ return hasPipeHandshakeCredential;
+ }
+
+ int getLoginCallCount() {
+ return loginCallCount;
+ }
+
void setFailFilePieceMemoryAllocation(final boolean
failFilePieceMemoryAllocation) {
this.failFilePieceMemoryAllocation = failFilePieceMemoryAllocation;
}
@@ -290,7 +369,8 @@ public class IoTDBFileReceiverTest {
@Override
protected TSStatus login() {
- return new TSStatus(200);
+ loginCallCount++;
+ return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
}
@Override
@@ -355,6 +435,19 @@ public class IoTDBFileReceiverTest {
}
}
+ private static class DummyHandshakeV2Req extends PipeTransferHandshakeV2Req {
+
+ static DummyHandshakeV2Req toTPipeTransferReq(final Map<String, String>
params)
+ throws IOException {
+ return (DummyHandshakeV2Req) new
DummyHandshakeV2Req().convertToTPipeTransferReq(params);
+ }
+
+ @Override
+ protected PipeRequestType getPlanType() {
+ return PipeRequestType.HANDSHAKE_DATANODE_V2;
+ }
+ }
+
private static class DummyFileSealReqV1 extends PipeTransferFileSealReqV1 {
static DummyFileSealReqV1 toTPipeTransferReq(final String fileName, final
long fileLength)