This is an automated email from the ASF dual-hosted git repository.
jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/dev/1.3 by this push:
new 912bf892f55 Improve pipe receiver session handling (#18084) (#18137)
912bf892f55 is described below
commit 912bf892f5527e54e7d01211c4bdc1f354d85dc2
Author: Caideyipi <[email protected]>
AuthorDate: Wed Jul 8 18:51:31 2026 +0800
Improve pipe receiver session handling (#18084) (#18137)
* Improve pipe receiver session handling
* Fix pipe receiver test body handling
* Internationalize two-stage receiver login failure
(cherry picked from commit 86624a137bb5448b0d5a5d50b1414ceb5b1bb81a)
---
.../receiver/protocol/IoTDBConfigNodeReceiver.java | 51 ++++++++++--
.../task/builder/PipeDataNodeTaskBuilder.java | 36 ++++++++
.../agent/task/stage/PipeTaskProcessorStage.java | 5 +-
.../receiver/TwoStageAggregateReceiver.java | 11 +++
.../exchange/sender/TwoStageAggregateSender.java | 75 +++++++++++++----
.../twostage/plugin/TwoStageCountProcessor.java | 9 +-
.../protocol/thrift/IoTDBDataNodeReceiver.java | 57 ++++++++++---
.../protocol/thrift/impl/ClientRPCServiceImpl.java | 42 +++++++++-
.../iotdb/db/pipe/sink/PipeReceiverTest.java | 70 ++++++++++++----
.../org/apache/iotdb/commons/audit/UserEntity.java | 67 +++++++++++++++
.../pipe/config/constant/PipeSourceConstant.java | 4 +
.../env/PipeTaskProcessorRuntimeEnvironment.java | 23 ++++++
.../commons/pipe/receiver/IoTDBFileReceiver.java | 76 ++++++++++++++---
.../common/PipeTransferHandshakeConstant.java | 2 +
.../pipe/receiver/IoTDBFileReceiverTest.java | 95 +++++++++++++++++++++-
15 files changed, 557 insertions(+), 66 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 aa32211e9af..64f4a26350c 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
@@ -57,7 +57,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;
@@ -123,13 +122,15 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
.setMessage(
"The receiver ConfigNode has set up a new receiver and
the sender must re-send its handshake request."));
}
- TPipeTransferResp resp;
- long startTime = System.nanoTime();
+ 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;
@@ -199,6 +200,34 @@ 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();
+ }
+ 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(
@@ -480,11 +509,19 @@ public class IoTDBConfigNodeReceiver extends
IoTDBFileReceiver {
@Override
protected boolean shouldLogin() {
- return lastSuccessfulLoginTime == Long.MIN_VALUE || super.shouldLogin();
+ 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).getStatus();
}
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 cc4456b721b..7ea16f888a1 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;
@@ -46,6 +47,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;
@@ -126,6 +128,8 @@ public class PipeDataNodeTaskBuilder {
sinkStage.getPipeSinkPendingQueue(),
PROCESSOR_EXECUTOR,
pipeTaskMeta,
+ getSourceUserEntity(sourceParameters),
+ getSourcePassword(sourceParameters),
pipeStaticMeta
.getConnectorParameters()
.getStringOrDefault(
@@ -138,6 +142,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 c5f58a248e2..e2607267fbd 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;
@@ -68,12 +69,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))
? PipeDataNodeAgent.plugin()
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 240f7a89e5f..f4ffa159afa 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
@@ -25,6 +25,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;
@@ -36,6 +38,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() {
@@ -45,6 +48,14 @@ 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,
+ "Log in failed. Either you are not authorized or the session
has 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 3c36559a300..53e826917d9 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;
@@ -31,14 +33,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;
@@ -54,8 +60,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 =
@@ -66,8 +76,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)
@@ -174,7 +191,7 @@ public class TwoStageAggregateSender implements
AutoCloseable {
try {
endPointIoTDBSyncClientMap.put(endPoint,
constructIoTDBSyncClient(endPoint));
- } catch (TTransportException e) {
+ } catch (TException e) {
LOGGER.warn("Failed to construct IoTDBSyncClient", e);
}
}
@@ -190,8 +207,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 {
@@ -205,17 +221,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 340ac085a01..c4a3acc50b3 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.index.ProgressIndex;
import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex;
import org.apache.iotdb.commons.consensus.index.impl.StateProgressIndex;
@@ -93,6 +94,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<>();
@@ -126,6 +130,8 @@ public class TwoStageCountProcessor implements
PipeProcessor {
creationTime = runtimeEnvironment.getCreationTime();
regionId = runtimeEnvironment.getRegionId();
pipeTaskMeta = runtimeEnvironment.getPipeTaskMeta();
+ sourceUserEntity = runtimeEnvironment.getSourceUserEntity();
+ sourcePassword = runtimeEnvironment.getSourcePassword();
outputSeries = parseOutputSeries(parameters);
@@ -156,7 +162,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 3cf0b434fea..761c0183c9b 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
@@ -54,7 +54,6 @@ import
org.apache.iotdb.db.pipe.receiver.visitor.PipeStatementTSStatusVisitor;
import org.apache.iotdb.db.pipe.receiver.visitor.PipeStatementToBatchVisitor;
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;
@@ -175,20 +174,15 @@ public class IoTDBDataNodeReceiver extends
IoTDBFileReceiver {
if (requestType != PipeRequestType.TRANSFER_SLICE) {
sliceReqHandler.clear();
}
+ 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);
@@ -432,6 +426,41 @@ 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();
+ }
+ return null;
+ }
+
+ return new TPipeTransferResp(getNotLoggedInStatus());
+ }
+
+ private static boolean requiresAuthentication(final PipeRequestType
requestType) {
+ switch (requestType) {
+ case TRANSFER_TABLET_INSERT_NODE:
+ case TRANSFER_TABLET_RAW:
+ case TRANSFER_TABLET_BINARY:
+ case TRANSFER_TABLET_BATCH:
+ 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_SCHEMA_PLAN:
+ 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
@@ -893,6 +922,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 9879e65bb4d..370e3ead82b 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
@@ -115,6 +115,8 @@ import org.apache.iotdb.db.utils.SchemaUtils;
import org.apache.iotdb.db.utils.SetThreadName;
import org.apache.iotdb.rpc.RpcUtils;
import org.apache.iotdb.rpc.TSStatusCode;
+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;
@@ -2812,17 +2814,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/audit/UserEntity.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/UserEntity.java
new file mode 100644
index 00000000000..7307b98d41a
--- /dev/null
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/UserEntity.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.commons.audit;
+
+import java.util.Objects;
+
+/** This class defines the fields of a user entity to be audited. */
+public class UserEntity {
+
+ private final long userId;
+ private final String username;
+ private final String cliHostname;
+
+ public UserEntity(final long userId, final String username, final String
cliHostname) {
+ this.userId = userId;
+ this.username = username;
+ this.cliHostname = cliHostname;
+ }
+
+ public long getUserId() {
+ return userId;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getCliHostname() {
+ return cliHostname;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final UserEntity that = (UserEntity) o;
+ return userId == that.userId
+ && Objects.equals(username, that.username)
+ && Objects.equals(cliHostname, that.cliHostname);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(userId, username, cliHostname);
+ }
+}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/config/constant/PipeSourceConstant.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/config/constant/PipeSourceConstant.java
index 5fe2f53b7f5..4a7ca7534e8 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/config/constant/PipeSourceConstant.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/config/constant/PipeSourceConstant.java
@@ -30,6 +30,10 @@ public class PipeSourceConstant {
public static final String SOURCE_IOTDB_USERNAME_KEY = "source.username";
public static final String EXTRACTOR_IOTDB_PASSWORD_KEY =
"extractor.password";
public static final String SOURCE_IOTDB_PASSWORD_KEY = "source.password";
+ public static final String EXTRACTOR_IOTDB_USER_ID = "extractor.user-id";
+ public static final String SOURCE_IOTDB_USER_ID = "source.user-id";
+ public static final String EXTRACTOR_IOTDB_CLI_HOSTNAME =
"extractor.cli-hostname";
+ public static final String SOURCE_IOTDB_CLI_HOSTNAME = "source.cli-hostname";
public static final String EXTRACTOR_INCLUSION_KEY = "extractor.inclusion";
public static final String SOURCE_INCLUSION_KEY = "source.inclusion";
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 7346ae2bccd..2375f0d4072 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
@@ -20,6 +20,7 @@
package org.apache.iotdb.commons.pipe.receiver;
import org.apache.iotdb.common.rpc.thrift.TSStatus;
+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.pipe.config.PipeConfig;
@@ -78,6 +79,8 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
protected String username = CONNECTOR_IOTDB_USER_DEFAULT_VALUE;
protected String password = CONNECTOR_IOTDB_PASSWORD_DEFAULT_VALUE;
+ protected UserEntity userEntity;
+ protected boolean hasPipeHandshakeCredential = false;
private static final long LOGIN_PERIODIC_VERIFICATION_INTERVAL_MS =
PipeConfig.getInstance().getPipeReceiverLoginPeriodicVerificationIntervalMs();
@@ -101,6 +104,8 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
}
protected TPipeTransferResp handleTransferHandshakeV1(final
PipeTransferHandshakeV1Req req) {
+ hasPipeHandshakeCredential = false;
+
if (!CommonDescriptor.getInstance()
.getConfig()
.getTimestampPrecision()
@@ -219,6 +224,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,18 +288,44 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
return new TPipeTransferResp(status);
}
+ long userId = -1;
+ String cliHostname = "";
+
+ final String userIdString =
+
req.getParams().get(PipeTransferHandshakeConstant.HANDSHAKE_KEY_USER_ID);
+ if (userIdString != null) {
+ userId = Long.parseLong(userIdString);
+ }
+ final String cliHostnameString =
+
req.getParams().get(PipeTransferHandshakeConstant.HANDSHAKE_KEY_CLI_HOSTNAME);
+ if (cliHostnameString != null) {
+ cliHostname = cliHostnameString;
+ }
+
final String usernameString =
req.getParams().get(PipeTransferHandshakeConstant.HANDSHAKE_KEY_USERNAME);
- if (usernameString != null) {
- username = usernameString;
- }
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,
"Receiver id = %s: Handshake failed because login failed, response
status = %s.",
@@ -300,6 +333,7 @@ public abstract class IoTDBFileReceiver implements
IoTDBReceiver {
status);
return new TPipeTransferResp(status);
} else {
+ lastSuccessfulLoginTime = System.currentTimeMillis();
LOGGER.info("Receiver id = {}: User {} login successfully.",
receiverId.get(), username);
}
@@ -334,13 +368,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();
@@ -369,6 +407,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/main/java/org/apache/iotdb/commons/pipe/sink/payload/thrift/common/PipeTransferHandshakeConstant.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/payload/thrift/common/PipeTransferHandshakeConstant.java
index 8976a25c5cc..5ed88837187 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/payload/thrift/common/PipeTransferHandshakeConstant.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/sink/payload/thrift/common/PipeTransferHandshakeConstant.java
@@ -27,6 +27,8 @@ public class PipeTransferHandshakeConstant {
public static final String HANDSHAKE_KEY_LOAD_TSFILE_STRATEGY =
"loadTsFileStrategy";
public static final String HANDSHAKE_KEY_USERNAME = "username";
public static final String HANDSHAKE_KEY_PASSWORD = "password";
+ public static final String HANDSHAKE_KEY_USER_ID = "userId";
+ public static final String HANDSHAKE_KEY_CLI_HOSTNAME = "cliHostname";
public static final String HANDSHAKE_KEY_VALIDATE_TSFILE = "validateTsFile";
public static final String HANDSHAKE_KEY_MARK_AS_PIPE_REQUEST =
"markAsPipeRequest";
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 c692cb1513a..b0180e5b124 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
@@ -22,10 +22,12 @@ package org.apache.iotdb.commons.pipe.receiver;
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.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.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.rpc.TSStatusCode;
import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq;
import org.apache.iotdb.service.rpc.thrift.TPipeTransferResp;
@@ -41,7 +43,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 {
@@ -115,6 +119,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");
@@ -142,6 +204,7 @@ public class IoTDBFileReceiverTest {
private final File receiverFileBaseDir;
private TSStatus loadFileV1Status = new
TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
+ private int loginCallCount = 0;
DummyFileReceiver(final File baseDir) {
receiverFileBaseDir = baseDir;
@@ -158,6 +221,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);
}
@@ -166,6 +233,18 @@ public class IoTDBFileReceiverTest {
loadFileV1Status = status;
}
+ void setHasPipeHandshakeCredential(final boolean
hasPipeHandshakeCredential) {
+ this.hasPipeHandshakeCredential = hasPipeHandshakeCredential;
+ }
+
+ boolean hasPipeHandshakeCredential() {
+ return hasPipeHandshakeCredential;
+ }
+
+ int getLoginCallCount() {
+ return loginCallCount;
+ }
+
TPipeTransferResp sealFileV1(final String fileName, final long fileLength)
throws IOException {
return
handleTransferFileSealV1(DummyFileSealReqV1.toTPipeTransferReq(fileName,
fileLength));
}
@@ -220,7 +299,8 @@ public class IoTDBFileReceiverTest {
@Override
protected TSStatus login() {
- return new TSStatus(200);
+ loginCallCount++;
+ return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
}
@Override
@@ -261,6 +341,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)