GH-3572: Migrate SFTP from jsch to sshd-sftp (#3892)
* GH-3572: Migrate SFTP from `jsch` to `sshd-sftp` Fixes https://github.com/spring-projects/spring-integration/issues/3572 * Rework SFTP module from the JSch API to more modern `sshd-sftp` * Migrate generics of most API from `ChannelSftp.LsEntry` to the `SftpClient.DirEntry` * Rework `DefaultSftpSessionFactory` to deal with an `SshClient` and create `SftpClient` wrapped to the `SftpSession` * Implement a `ResourceKnownHostsServerKeyVerifier` to load `known-hosts` from any possible resource * Implement an expected `SftpSession.list()` with just file name to take or pattern matching * Remove some unused tests and their config * Remove tests for custom `UserInfo` since we don't provide any custom out-of-the-box * Test a new `ResourceKnownHostsServerKeyVerifier` against default `known-hosts` file * * Some tests improvements * * Improve generics handling for `FileUtils`
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,7 @@ import java.util.Collections;
|
||||
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
|
||||
import org.apache.sshd.server.SshServer;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.apache.sshd.sftp.server.SftpSubsystemFactory;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -32,8 +33,6 @@ import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.sftp.server.ApacheMinaSftpEventListener;
|
||||
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* Provides an embedded SFTP Server for test cases.
|
||||
*
|
||||
@@ -77,14 +76,16 @@ public class SftpTestSupport extends RemoteFileTestSupport {
|
||||
port = server.getPort();
|
||||
}
|
||||
|
||||
public static SessionFactory<LsEntry> sessionFactory() {
|
||||
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
|
||||
public static SessionFactory<SftpClient.DirEntry> sessionFactory() {
|
||||
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(false);
|
||||
factory.setHost("localhost");
|
||||
factory.setPort(port);
|
||||
factory.setUser("foo");
|
||||
factory.setPassword("foo");
|
||||
factory.setAllowUnknownKeys(true);
|
||||
return new CachingSessionFactory<>(factory);
|
||||
CachingSessionFactory<SftpClient.DirEntry> cachingSessionFactory = new CachingSessionFactory<>(factory);
|
||||
cachingSessionFactory.setTestSession(true);
|
||||
return cachingSessionFactory;
|
||||
}
|
||||
|
||||
public static ApacheMinaSftpEventListener eventListener() {
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
<beans:property name="password" value="hello"/>
|
||||
<beans:property name="port" value="2222"/>
|
||||
<beans:property name="user" value="oleg"/>
|
||||
<beans:property name="sessionConfig" ref="sessionConfig"/>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="csf" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,8 +18,6 @@ package org.springframework.integration.sftp.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -38,22 +36,23 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
@DirtiesContext
|
||||
public class InboundChannelAdapterParserCachingTests {
|
||||
|
||||
@Autowired private Object cachingAdapter;
|
||||
@Autowired
|
||||
private Object cachingAdapter;
|
||||
|
||||
@Autowired private Object nonCachingAdapter;
|
||||
@Autowired
|
||||
private Object nonCachingAdapter;
|
||||
|
||||
@Test
|
||||
public void cachingAdapter() {
|
||||
Object sessionFactory = TestUtils.getPropertyValue(cachingAdapter, "source.synchronizer.remoteFileTemplate.sessionFactory");
|
||||
Object sessionFactory =
|
||||
TestUtils.getPropertyValue(cachingAdapter, "source.synchronizer.remoteFileTemplate.sessionFactory");
|
||||
assertThat(sessionFactory.getClass()).isEqualTo(CachingSessionFactory.class);
|
||||
Properties sessionConfig = TestUtils.getPropertyValue(sessionFactory, "sessionFactory.sessionConfig", Properties.class);
|
||||
assertThat(sessionConfig).isNotNull();
|
||||
assertThat(sessionConfig.getProperty("StrictHostKeyChecking")).isEqualTo("no");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonCachingAdapter() {
|
||||
Object sessionFactory = TestUtils.getPropertyValue(nonCachingAdapter, "source.synchronizer.remoteFileTemplate.sessionFactory");
|
||||
Object sessionFactory =
|
||||
TestUtils.getPropertyValue(nonCachingAdapter, "source.synchronizer.remoteFileTemplate.sessionFactory");
|
||||
assertThat(sessionFactory.getClass()).isEqualTo(DefaultSftpSessionFactory.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
|
||||
|
||||
|
||||
|
||||
http://www.springframework.org/schema/integration/sftp https://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
|
||||
|
||||
<channel id="requestChannel"/>
|
||||
|
||||
<beans:bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.SimpleSftpSessionFactory">
|
||||
<beans:property name="host" value="localhost"/>
|
||||
<beans:property name="knownHostsResource" value="local, foo.com, bar.foo"/>
|
||||
<beans:property name="privateKey" value="classpath:org/springframework/integration/sftp/config/sftpTest"/>
|
||||
<beans:property name="privateKeyPassphrase" value="ghj"/>
|
||||
<beans:property name="password" value="hello"/>
|
||||
<beans:property name="port" value="2222"/>
|
||||
<beans:property name="user" value="oleg"/>
|
||||
</beans:bean>
|
||||
|
||||
<sftp:inbound-channel-adapter id="sftpAdapterNoAutoCreate"
|
||||
channel="requestChannel"
|
||||
session-factory="sftpSessionFactory"
|
||||
filter="filter"
|
||||
filename-pattern="."
|
||||
auto-startup="false"
|
||||
remote-directory="/foo"
|
||||
local-directory="file:local-test-dir"
|
||||
auto-create-local-directory="false"
|
||||
auto-delete-remote-files-on-sync="false">
|
||||
<poller fixed-rate="1000"/>
|
||||
</sftp:inbound-channel-adapter>
|
||||
|
||||
<beans:bean id="filter" class="org.springframework.integration.sftp.filters.SftpPatternMatchingFileListFilter">
|
||||
<beans:constructor-arg value="."/>
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
@@ -126,16 +125,6 @@ public class InboundChannelAdapterParserTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
//exactly one of 'filename-pattern' or 'filter' is allowed on SFTP inbound adapter
|
||||
public void testFailWithFilePatternAndFilter() {
|
||||
assertThat(!new File("target/bar").exists()).isTrue();
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() ->
|
||||
new ClassPathXmlApplicationContext("InboundChannelAdapterParserTests-context-fail.xml",
|
||||
getClass()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalDirAutoCreated() {
|
||||
assertThat(new File("foo").exists()).isFalse();
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans
|
||||
xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/sftp https://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
|
||||
|
||||
<channel id="inboundFilesChannel"/>
|
||||
|
||||
<beans:bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<beans:property name="host" value="localhost"/>
|
||||
<beans:property name="knownHostsResource"
|
||||
value="#{ new org.springframework.core.io.ByteArrayResource('local, foo.com, bar.foo'.bytes)}"/>
|
||||
<beans:property name="privateKey" value="classpath:org/springframework/integration/sftp/config/sftpTest"/>
|
||||
<beans:property name="privateKeyPassphrase" value="ghj"/>
|
||||
<beans:property name="password" value="hello"/>
|
||||
<beans:property name="port" value="2222"/>
|
||||
<beans:property name="user" value="oleg"/>
|
||||
</beans:bean>
|
||||
|
||||
<sftp:inbound-channel-adapter id="sftpAdapter"
|
||||
session-factory="sftpSessionFactory"
|
||||
auto-startup="false"
|
||||
remote-directory="/hello"
|
||||
local-directory="file:local-test-dir"
|
||||
channel="inboundFilesChannel"
|
||||
filename-pattern=".*?jpg"
|
||||
auto-create-local-directory="true"
|
||||
delete-remote-files="true">
|
||||
<poller fixed-rate="1000" />
|
||||
</sftp:inbound-channel-adapter>
|
||||
|
||||
</beans:beans>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/sftp https://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
|
||||
|
||||
<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.SimpleSftpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="knownHostsResource" value="local, foo.com, bar.foo"/>
|
||||
<property name="privateKey" value="classpath:org/springframework/integration/sftp/config/sftpTest"/>
|
||||
<property name="privateKeyPassphrase" value="ghj"/>
|
||||
<property name="password" value="hello"/>
|
||||
<property name="port" value="2222"/>
|
||||
<property name="user" value="oleg"/>
|
||||
</bean>
|
||||
|
||||
<int:channel id="inputChannel"/>
|
||||
|
||||
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapterWithExpression"
|
||||
session-factory="sftpSessionFactory"
|
||||
channel="inputChannel"
|
||||
charset="UTF-8"
|
||||
remote-filename-generator="fileNameGenerator"
|
||||
remote-directory-expression="'foo' + '/' + 'bar'"
|
||||
remote-filename-generator-expression="payload.getName() + '-foo'"/>
|
||||
|
||||
|
||||
<bean id="fileNameGenerator" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="org.springframework.integration.file.FileNameGenerator"/>
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -157,15 +157,6 @@ public class OutboundChannelAdapterParserTests {
|
||||
.withMessageContaining("Only one of 'remote-directory'");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailWithFileExpressionAndFileGenerator() {
|
||||
assertThatExceptionOfType(BeanDefinitionStoreException.class)
|
||||
.isThrownBy(() ->
|
||||
new ClassPathXmlApplicationContext(
|
||||
"OutboundChannelAdapterParserTests-context-fail-fileFileGen.xml",
|
||||
getClass()));
|
||||
}
|
||||
|
||||
public static class FooAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.integration.sftp.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousy
|
||||
* @author Gunnar Hillert
|
||||
*
|
||||
*/
|
||||
public class SftpInboundOutboundSanitySample {
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testInbound() throws Exception {
|
||||
File fileA = new File("local-test-dir/a.test");
|
||||
if (fileA.exists()) {
|
||||
fileA.delete();
|
||||
}
|
||||
File fileB = new File("local-test-dir/b.test");
|
||||
if (fileB.exists()) {
|
||||
fileB.delete();
|
||||
}
|
||||
fileA = new File("remote-target-dir/a.test-foo");
|
||||
if (fileA.exists()) {
|
||||
fileA.delete();
|
||||
}
|
||||
fileB = new File("remote-target-dir/b.test-foo");
|
||||
if (fileB.exists()) {
|
||||
fileB.delete();
|
||||
}
|
||||
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"SftpInboundReceiveSample-ignored.xml", this.getClass());
|
||||
Thread.sleep(5000);
|
||||
fileA = new File("local-test-dir/a.test");
|
||||
fileB = new File("local-test-dir/b.test");
|
||||
assertThat(fileA.exists()).isTrue();
|
||||
assertThat(fileB.exists()).isTrue();
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testOutbound() throws Exception {
|
||||
ClassPathXmlApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("SftpOutboundTransferSample-ignored.xml", this.getClass());
|
||||
File fileA = new File("local-test-dir/a.test");
|
||||
File fileB = new File("local-test-dir/b.test");
|
||||
MessageChannel ftpChannel = ac.getBean("ftpChannel", MessageChannel.class);
|
||||
ftpChannel.send(new GenericMessage<File>(fileA));
|
||||
ftpChannel.send(new GenericMessage<File>(fileB));
|
||||
Thread.sleep(6000);
|
||||
fileA = new File("remote-target-dir/a.test-foo");
|
||||
fileB = new File("remote-target-dir/b.test-foo");
|
||||
assertThat(fileA.exists()).isTrue();
|
||||
assertThat(fileB.exists()).isTrue();
|
||||
ac.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xmlns:task="http://www.springframework.org/schema/task"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/task https://www.springframework.org/schema/task/spring-task.xsd
|
||||
http://www.springframework.org/schema/integration/sftp https://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
|
||||
|
||||
<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<property name="privateKey" value="classpath:org/springframework/integration/sftp/config/sftp_rsa"/>
|
||||
<property name="privateKeyPassphrase" value="springintegration"/>
|
||||
<property name="port" value="22"/>
|
||||
<property name="user" value="ozhurakousky"/>
|
||||
</bean>
|
||||
|
||||
<int-sftp:inbound-channel-adapter id="sftpInbondAdapter"
|
||||
channel="receiveChannel"
|
||||
session-factory="sftpSessionFactory"
|
||||
local-directory="file:local-test-dir"
|
||||
remote-directory="/Users/ozhurakousky/workspace-sts-2.3.3.M2/si/spring-integration/spring-integration-sftp/remote-test-dir"
|
||||
auto-startup="true"
|
||||
temporary-file-suffix=".foo"
|
||||
delete-remote-files="false"
|
||||
filename-regex=".*\.test$">
|
||||
<int:poller fixed-rate="3000" max-messages-per-poll="1" />
|
||||
</int-sftp:inbound-channel-adapter>
|
||||
|
||||
<int:channel id="receiveChannel">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
<task:executor id="executor" pool-size="15"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.integration.sftp.config;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class SftpMessageHistoryTests {
|
||||
|
||||
@Test
|
||||
public void testMessageHistoryCompliance() {
|
||||
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("MessageHistory-context.xml",
|
||||
SftpMessageHistoryTests.class);
|
||||
SourcePollingChannelAdapter spca = ac.getBean("sftpAdapter", SourcePollingChannelAdapter.class);
|
||||
assertThat(spca.getComponentName()).isEqualTo("sftpAdapter");
|
||||
assertThat(spca.getComponentType()).isEqualTo("sftp:inbound-channel-adapter");
|
||||
ac.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/sftp https://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
|
||||
|
||||
<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
<!-- <property name="privateKey" value="classpath:org/springframework/integration/sftp/config/sftp_rsa"/> -->
|
||||
<!-- <property name="privateKeyPassphrase" value="springintegration"/> -->
|
||||
<property name="port" value="22"/>
|
||||
<property name="user" value="ozhurakousky"/>
|
||||
<property name="password" value="password"/>
|
||||
</bean>
|
||||
|
||||
<int:channel id="ftpChannel"/>
|
||||
|
||||
<int-sftp:outbound-channel-adapter id="sftpOutboundAdapter"
|
||||
session-factory="sftpSessionFactory"
|
||||
channel="ftpChannel"
|
||||
charset="UTF-8"
|
||||
temporary-file-suffix=".foo"
|
||||
remote-filename-generator-expression="payload.getName() + '-foo'"
|
||||
auto-create-directory="true"
|
||||
remote-directory="spring-integration-sftp/remote-target-dir/bar/baz"/>
|
||||
|
||||
</beans>
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.integration.sftp.config;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
*
|
||||
*/
|
||||
public class SftpOutboundTransferSample {
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void testOutbound() throws Exception {
|
||||
ClassPathXmlApplicationContext ac =
|
||||
new ClassPathXmlApplicationContext("SftpOutboundTransferSample-ignored.xml", SftpOutboundTransferSample.class);
|
||||
ac.start();
|
||||
File file = new File("/Users/ozhurakousky/workspace-sts-2.3.3.M2/si/spring-integration/spring-integration-sftp/local-test-dir/foo.txt");
|
||||
if (file.exists()) {
|
||||
Message<File> message = MessageBuilder.withPayload(file).build();
|
||||
MessageChannel inputChannel = ac.getBean("inputChannel", MessageChannel.class);
|
||||
inputChannel.send(message);
|
||||
Thread.sleep(2000);
|
||||
}
|
||||
ac.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,10 +20,14 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.apache.sshd.sftp.common.SftpHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||
import org.junit.jupiter.api.condition.OS;
|
||||
@@ -50,8 +54,6 @@ import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
@@ -68,7 +70,6 @@ public class SftpTests extends SftpTestSupport {
|
||||
private IntegrationFlowContext flowContext;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testSftpInboundFlow() {
|
||||
QueueChannel out = new QueueChannel();
|
||||
IntegrationFlow flow = IntegrationFlow
|
||||
@@ -78,7 +79,7 @@ public class SftpTests extends SftpTestSupport {
|
||||
.regexFilter(".*\\.txt$")
|
||||
.localFilenameExpression("#this.toUpperCase() + '.a'")
|
||||
.localDirectory(getTargetLocalDirectory())
|
||||
.remoteComparator(Comparator.naturalOrder()),
|
||||
.remoteComparator(Comparator.comparing(SftpClient.DirEntry::getFilename)),
|
||||
e -> e.id("sftpInboundAdapter").poller(Pollers.fixedDelay(100)))
|
||||
.channel(out)
|
||||
.get();
|
||||
@@ -122,7 +123,7 @@ public class SftpTests extends SftpTestSupport {
|
||||
assertThat(message).isNotNull();
|
||||
assertThat(message.getPayload()).isInstanceOf(InputStream.class);
|
||||
assertThat(message.getHeaders().get(FileHeaders.REMOTE_FILE)).isIn(" sftpSource1.txt", "sftpSource2.txt");
|
||||
assertThat(message.getHeaders().get(FileHeaders.REMOTE_HOST_PORT, String.class)).contains("localhost:");
|
||||
assertThat(message.getHeaders().get(FileHeaders.REMOTE_HOST_PORT, String.class)).contains("localhost");
|
||||
((InputStream) message.getPayload()).close();
|
||||
new IntegrationMessageHeaderAccessor(message).getCloseableResource().close();
|
||||
|
||||
@@ -141,11 +142,11 @@ public class SftpTests extends SftpTestSupport {
|
||||
.setHeader(FileHeaders.FILENAME, fileName)
|
||||
.build());
|
||||
|
||||
RemoteFileTemplate<ChannelSftp.LsEntry> template = new RemoteFileTemplate<>(sessionFactory());
|
||||
ChannelSftp.LsEntry[] files = template.execute(session ->
|
||||
session.list(getTargetRemoteDirectory().getName() + "/" + fileName));
|
||||
RemoteFileTemplate<SftpClient.DirEntry> template = new RemoteFileTemplate<>(sessionFactory());
|
||||
SftpClient.DirEntry[] files =
|
||||
template.execute(session -> session.list(getTargetRemoteDirectory().getName() + "/" + fileName));
|
||||
assertThat(files.length).isEqualTo(1);
|
||||
assertThat(files[0].getAttrs().getSize()).isEqualTo(3);
|
||||
assertThat(files[0].getAttributes().getSize()).isEqualTo(3);
|
||||
|
||||
registration.destroy();
|
||||
}
|
||||
@@ -163,10 +164,10 @@ public class SftpTests extends SftpTestSupport {
|
||||
.setHeader(FileHeaders.FILENAME, fileName)
|
||||
.build());
|
||||
|
||||
ChannelSftp.LsEntry[] files = sftpTemplate.execute(session ->
|
||||
session.list(getTargetRemoteDirectory().getName() + "/" + fileName));
|
||||
SftpClient.DirEntry[] files =
|
||||
sftpTemplate.execute(session -> session.list(getTargetRemoteDirectory().getName() + "/" + fileName));
|
||||
assertThat(files.length).isEqualTo(1);
|
||||
assertThat(files[0].getAttrs().getSize()).isEqualTo(3);
|
||||
assertThat(files[0].getAttributes().getSize()).isEqualTo(3);
|
||||
|
||||
registration.destroy();
|
||||
}
|
||||
@@ -187,10 +188,10 @@ public class SftpTests extends SftpTestSupport {
|
||||
.setHeader(FileHeaders.FILENAME, fileName)
|
||||
.build());
|
||||
|
||||
ChannelSftp.LsEntry[] files = sftpTemplate.execute(session ->
|
||||
session.list(getTargetRemoteDirectory().getName() + "/" + fileName));
|
||||
SftpClient.DirEntry[] files =
|
||||
sftpTemplate.execute(session -> session.list(getTargetRemoteDirectory().getName() + "/" + fileName));
|
||||
assertThat(files.length).isEqualTo(1);
|
||||
assertThat(files[0].getAttrs().getSize()).isEqualTo(6);
|
||||
assertThat(files[0].getAttributes().getSize()).isEqualTo(6);
|
||||
|
||||
registration.destroy();
|
||||
}
|
||||
@@ -210,16 +211,17 @@ public class SftpTests extends SftpTestSupport {
|
||||
.setHeader(FileHeaders.FILENAME, fileName)
|
||||
.build());
|
||||
|
||||
RemoteFileTemplate<ChannelSftp.LsEntry> template = new RemoteFileTemplate<>(sessionFactory());
|
||||
ChannelSftp.LsEntry[] files = template.execute(session ->
|
||||
session.list(getTargetRemoteDirectory().getName() + "/" + fileName));
|
||||
RemoteFileTemplate<SftpClient.DirEntry> template = new RemoteFileTemplate<>(sessionFactory());
|
||||
SftpClient.DirEntry[] files =
|
||||
template.execute(session -> session.list(getTargetRemoteDirectory().getName() + "/" + fileName));
|
||||
assertThat(files.length).isEqualTo(1);
|
||||
assertThat(files[0].getAttrs().getSize()).isEqualTo(3);
|
||||
String[] permissions = files[0].getAttrs().getPermissionsString().substring(1).replaceAll("--", "-").split("-");
|
||||
assertThat(permissions[0]).isEqualTo("rw");
|
||||
assertThat(permissions[1]).isEqualTo("r");
|
||||
assertThat(permissions[2]).isEqualTo("r");
|
||||
|
||||
assertThat(files[0].getAttributes().getSize()).isEqualTo(3);
|
||||
int permissionFlags = files[0].getAttributes().getPermissions();
|
||||
Set<PosixFilePermission> posixFilePermissions = SftpHelper.permissionsToAttributes(permissionFlags);
|
||||
assertThat(posixFilePermissions)
|
||||
.contains(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE,
|
||||
PosixFilePermission.GROUP_READ, PosixFilePermission.OTHERS_READ)
|
||||
.doesNotContain(PosixFilePermission.GROUP_WRITE, PosixFilePermission.OTHERS_WRITE);
|
||||
registration.destroy();
|
||||
}
|
||||
|
||||
@@ -264,9 +266,9 @@ public class SftpTests extends SftpTestSupport {
|
||||
Message<?> receive = out.receive(10_000);
|
||||
assertThat(receive).isNotNull();
|
||||
Object payload = receive.getPayload();
|
||||
assertThat(payload).isInstanceOf(ChannelSftp.LsEntry[].class);
|
||||
assertThat(payload).isInstanceOf(SftpClient.DirEntry[].class);
|
||||
|
||||
assertThat(((ChannelSftp.LsEntry[]) payload).length > 0).isTrue();
|
||||
assertThat(((SftpClient.DirEntry[]) payload).length > 0).isTrue();
|
||||
|
||||
registration.destroy();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -32,10 +33,10 @@ import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 5.0
|
||||
*
|
||||
*/
|
||||
@@ -50,9 +51,9 @@ public class SftpFileListFilterTests extends SftpTestSupport {
|
||||
public void testMarkerFile() throws Exception {
|
||||
SftpSystemMarkerFilePresentFileListFilter filter = new SftpSystemMarkerFilePresentFileListFilter(
|
||||
new SftpSimplePatternFileListFilter("*.txt"));
|
||||
LsEntry[] files = template.list("sftpSource");
|
||||
SftpClient.DirEntry[] files = template.list("sftpSource");
|
||||
assertThat(files.length).isGreaterThan(0);
|
||||
List<LsEntry> filtered = filter.filterFiles(files);
|
||||
List<SftpClient.DirEntry> filtered = filter.filterFiles(files);
|
||||
assertThat(filtered.size()).isEqualTo(0);
|
||||
File remoteDir = getSourceRemoteDirectory();
|
||||
File marker = new File(remoteDir, "sftpSource2.txt.complete");
|
||||
@@ -68,12 +69,12 @@ public class SftpFileListFilterTests extends SftpTestSupport {
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
public SessionFactory<LsEntry> sftpSessionFactory() {
|
||||
public SessionFactory<SftpClient.DirEntry> sftpSessionFactory() {
|
||||
return SftpFileListFilterTests.sessionFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SftpRemoteFileTemplate remoteFileTempalte() {
|
||||
public SftpRemoteFileTemplate remoteFileTemplate() {
|
||||
return new SftpRemoteFileTemplate(sftpSessionFactory());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,23 +17,22 @@
|
||||
package org.springframework.integration.sftp.filters;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author David Liu
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 4.0.4
|
||||
*
|
||||
*/
|
||||
@@ -43,18 +42,15 @@ public class SftpPersistentAcceptOnceFileListFilterTests {
|
||||
public void testRollback() throws Exception {
|
||||
SftpPersistentAcceptOnceFileListFilter filter = new SftpPersistentAcceptOnceFileListFilter(
|
||||
new SimpleMetadataStore(), "rollback:");
|
||||
ChannelSftp channel = new ChannelSftp();
|
||||
SftpATTRS attrs = mock(SftpATTRS.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Constructor<LsEntry> ctor = (Constructor<LsEntry>) LsEntry.class.getDeclaredConstructors()[0];
|
||||
ctor.setAccessible(true);
|
||||
LsEntry sftpFile1 = ctor.newInstance(channel, "foo", "foo", attrs);
|
||||
LsEntry sftpFile2 = ctor.newInstance(channel, "bar", "bar", attrs);
|
||||
LsEntry ftpFile3 = ctor.newInstance(channel, "baz", "baz", attrs);
|
||||
LsEntry[] files = new LsEntry[] {sftpFile1, sftpFile2, ftpFile3};
|
||||
List<LsEntry> passed = filter.filterFiles(files);
|
||||
SftpClient.Attributes attrs = new SftpClient.Attributes();
|
||||
attrs.setModifyTime(FileTime.from(Instant.now()));
|
||||
SftpClient.DirEntry sftpFile1 = new SftpClient.DirEntry("foo", "foo", attrs);
|
||||
SftpClient.DirEntry sftpFile2 = new SftpClient.DirEntry("bar", "bar", attrs);
|
||||
SftpClient.DirEntry sftpFile3 = new SftpClient.DirEntry("baz", "baz", attrs);
|
||||
SftpClient.DirEntry[] files = new SftpClient.DirEntry[]{ sftpFile1, sftpFile2, sftpFile3 };
|
||||
List<SftpClient.DirEntry> passed = filter.filterFiles(files);
|
||||
assertThat(Arrays.equals(files, passed.toArray())).isTrue();
|
||||
List<LsEntry> now = filter.filterFiles(files);
|
||||
List<SftpClient.DirEntry> now = filter.filterFiles(files);
|
||||
assertThat(now.size()).isEqualTo(0);
|
||||
filter.rollback(passed.get(1), passed);
|
||||
now = filter.filterFiles(files);
|
||||
@@ -70,15 +66,12 @@ public class SftpPersistentAcceptOnceFileListFilterTests {
|
||||
public void testKeyUsingFileName() throws Exception {
|
||||
SftpPersistentAcceptOnceFileListFilter filter = new SftpPersistentAcceptOnceFileListFilter(
|
||||
new SimpleMetadataStore(), "rollback:");
|
||||
ChannelSftp channel = new ChannelSftp();
|
||||
SftpATTRS attrs = mock(SftpATTRS.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Constructor<LsEntry> ctor = (Constructor<LsEntry>) LsEntry.class.getDeclaredConstructors()[0];
|
||||
ctor.setAccessible(true);
|
||||
LsEntry sftpFile1 = ctor.newInstance(channel, "foo", "same", attrs);
|
||||
LsEntry sftpFile2 = ctor.newInstance(channel, "bar", "same", attrs);
|
||||
LsEntry[] files = new LsEntry[] {sftpFile1, sftpFile2};
|
||||
List<LsEntry> now = filter.filterFiles(files);
|
||||
SftpClient.Attributes attrs = new SftpClient.Attributes();
|
||||
attrs.setModifyTime(FileTime.from(Instant.now()));
|
||||
SftpClient.DirEntry sftpFile1 = new SftpClient.DirEntry("foo", "same", attrs);
|
||||
SftpClient.DirEntry sftpFile2 = new SftpClient.DirEntry("bar", "same", attrs);
|
||||
SftpClient.DirEntry[] files = new SftpClient.DirEntry[]{ sftpFile1, sftpFile2 };
|
||||
List<SftpClient.DirEntry> now = filter.filterFiles(files);
|
||||
assertThat(now.size()).isEqualTo(2);
|
||||
assertThat(now.get(0).getFilename()).isEqualTo("foo");
|
||||
assertThat(now.get(1).getFilename()).isEqualTo("bar");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2020 the original author or authors.
|
||||
* Copyright 2015-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -34,11 +35,10 @@ import org.springframework.integration.sftp.SftpTestSupport;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 4.1.7
|
||||
*
|
||||
*/
|
||||
@@ -85,12 +85,13 @@ public class RollbackLocalFilterTests extends SftpTestSupport {
|
||||
this.file = in;
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
public SessionFactory<LsEntry> sftpSessionFactory() {
|
||||
public SessionFactory<SftpClient.DirEntry> sftpSessionFactory() {
|
||||
return RollbackLocalFilterTests.sessionFactory();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.springframework.integration.sftp.inbound;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.willReturn;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
@@ -27,13 +27,15 @@ import static org.mockito.Mockito.when;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -53,10 +55,6 @@ import org.springframework.integration.sftp.session.SftpTestSessionFactory;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
@@ -68,14 +66,9 @@ import com.jcraft.jsch.SftpATTRS;
|
||||
*/
|
||||
public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
|
||||
private static final com.jcraft.jsch.Session jschSession = mock(com.jcraft.jsch.Session.class);
|
||||
|
||||
@BeforeEach
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
willReturn("::1")
|
||||
.given(jschSession)
|
||||
.getHost();
|
||||
File file = new File("test");
|
||||
if (file.exists()) {
|
||||
String[] files = file.list();
|
||||
@@ -104,12 +97,11 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
PropertiesPersistingMetadataStore store = spy(new PropertiesPersistingMetadataStore());
|
||||
store.setBaseDirectory("test");
|
||||
store.afterPropertiesSet();
|
||||
SftpPersistentAcceptOnceFileListFilter persistFilter =
|
||||
new SftpPersistentAcceptOnceFileListFilter(store, "foo");
|
||||
List<FileListFilter<LsEntry>> filters = new ArrayList<>();
|
||||
SftpPersistentAcceptOnceFileListFilter persistFilter = new SftpPersistentAcceptOnceFileListFilter(store, "foo");
|
||||
List<FileListFilter<SftpClient.DirEntry>> filters = new ArrayList<>();
|
||||
filters.add(persistFilter);
|
||||
filters.add(patternFilter);
|
||||
CompositeFileListFilter<LsEntry> filter = new CompositeFileListFilter<>(filters);
|
||||
CompositeFileListFilter<SftpClient.DirEntry> filter = new CompositeFileListFilter<>(filters);
|
||||
synchronizer.setFilter(filter);
|
||||
synchronizer.setBeanFactory(mock(BeanFactory.class));
|
||||
synchronizer.afterPropertiesSet();
|
||||
@@ -140,7 +132,7 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
TestUtils.getPropertyValue(synchronizer, "remoteFileMetadataStore.metadata", Map.class);
|
||||
|
||||
String next = remoteFileMetadataStore.values().iterator().next();
|
||||
assertThat(URI.create(next).getHost()).isEqualTo("[::1]");
|
||||
assertThat(URI.create(next).getHost()).isEqualTo("mock.sftp.host");
|
||||
|
||||
Message<File> btestFile = ms.receive();
|
||||
assertThat(btestFile).isNotNull();
|
||||
@@ -172,43 +164,40 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
|
||||
public static class TestSftpSessionFactory extends DefaultSftpSessionFactory {
|
||||
|
||||
private final Vector<LsEntry> sftpEntries = new Vector<>();
|
||||
private List<SftpClient.DirEntry> sftpEntries;
|
||||
|
||||
private void init() {
|
||||
String[] files = new File("remote-test-dir").list();
|
||||
for (String fileName : files) {
|
||||
LsEntry lsEntry = mock(LsEntry.class);
|
||||
SftpATTRS attributes = mock(SftpATTRS.class);
|
||||
when(lsEntry.getAttrs()).thenReturn(attributes);
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.DATE, 1);
|
||||
when(lsEntry.getAttrs().getMTime())
|
||||
.thenReturn(Long.valueOf(calendar.getTimeInMillis() / 1000).intValue());
|
||||
when(lsEntry.getFilename()).thenReturn(fileName);
|
||||
when(lsEntry.getLongname()).thenReturn(fileName);
|
||||
sftpEntries.add(lsEntry);
|
||||
}
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.DATE, 1);
|
||||
this.sftpEntries =
|
||||
Arrays.stream(files)
|
||||
.map((file) -> {
|
||||
SftpClient.Attributes attributes = spy(new SftpClient.Attributes());
|
||||
attributes.setModifyTime(FileTime.fromMillis(calendar.getTimeInMillis()));
|
||||
given(attributes.isRegularFile()).willReturn(true);
|
||||
return new SftpClient.DirEntry(file, file, attributes);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SftpSession getSession() {
|
||||
if (this.sftpEntries.size() == 0) {
|
||||
this.init();
|
||||
if (this.sftpEntries == null) {
|
||||
init();
|
||||
}
|
||||
|
||||
try {
|
||||
ChannelSftp channel = mock(ChannelSftp.class);
|
||||
SftpClient sftpClient = mock(SftpClient.class);
|
||||
|
||||
String[] files = new File("remote-test-dir").list();
|
||||
for (String fileName : files) {
|
||||
when(channel.get("remote-test-dir/" + fileName))
|
||||
when(sftpClient.read("remote-test-dir/" + fileName))
|
||||
.thenReturn(new FileInputStream("remote-test-dir/" + fileName));
|
||||
}
|
||||
when(channel.ls("remote-test-dir")).thenReturn(sftpEntries);
|
||||
when(sftpClient.readDir("remote-test-dir")).thenReturn(this.sftpEntries);
|
||||
|
||||
when(jschSession.openChannel("sftp")).thenReturn(channel);
|
||||
return SftpTestSessionFactory.createSftpSession(jschSession);
|
||||
return SftpTestSessionFactory.createSftpSession(sftpClient);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create mock sftp session", e);
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Comparator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -53,8 +54,6 @@ import org.springframework.scheduling.support.PeriodicTrigger;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
@@ -120,7 +119,7 @@ public class SftpStreamingMessageSourceTests extends SftpTestSupport {
|
||||
received = (Message<byte[]>) this.data.receive(10000);
|
||||
assertThat(received).isNotNull();
|
||||
assertThat(received.getHeaders().get(FileHeaders.REMOTE_FILE_INFO)).isInstanceOf(SftpFileInfo.class);
|
||||
assertThat(received.getHeaders().get(FileHeaders.REMOTE_HOST_PORT, String.class)).contains("localhost:");
|
||||
assertThat(received.getHeaders().get(FileHeaders.REMOTE_HOST_PORT, String.class)).contains("localhost");
|
||||
this.adapter.stop();
|
||||
}
|
||||
|
||||
@@ -172,7 +171,7 @@ public class SftpStreamingMessageSourceTests extends SftpTestSupport {
|
||||
private SftpStreamingMessageSource buildSource() {
|
||||
SftpStreamingMessageSource messageSource =
|
||||
new SftpStreamingMessageSource(this.config.template(),
|
||||
Comparator.comparing(LsEntry::getFilename));
|
||||
Comparator.comparing(SftpClient.DirEntry::getFilename));
|
||||
messageSource.setRemoteDirectory("sftpSource/");
|
||||
messageSource.setBeanFactory(this.context);
|
||||
return messageSource;
|
||||
@@ -205,7 +204,7 @@ public class SftpStreamingMessageSourceTests extends SftpTestSupport {
|
||||
@InboundChannelAdapter(channel = "stream", autoStartup = "false")
|
||||
public MessageSource<InputStream> sftpMessageSource() {
|
||||
SftpStreamingMessageSource messageSource = new SftpStreamingMessageSource(template(),
|
||||
Comparator.comparing(LsEntry::getFilename));
|
||||
Comparator.comparing(SftpClient.DirEntry::getFilename));
|
||||
messageSource.setFilter(
|
||||
new SftpPersistentAcceptOnceFileListFilter(
|
||||
new SimpleMetadataStore(metadataMap()), "testStreaming"));
|
||||
@@ -225,7 +224,7 @@ public class SftpStreamingMessageSourceTests extends SftpTestSupport {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SessionFactory<LsEntry> ftpSessionFactory() {
|
||||
public SessionFactory<SftpClient.DirEntry> ftpSessionFactory() {
|
||||
return SftpStreamingMessageSourceTests.sessionFactory();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,41 +22,42 @@ import static org.mockito.AdditionalMatchers.and;
|
||||
import static org.mockito.AdditionalMatchers.not;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.willAnswer;
|
||||
import static org.mockito.BDDMockito.willReturn;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.sshd.common.SshConstants;
|
||||
import org.apache.sshd.common.SshException;
|
||||
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
|
||||
import org.apache.sshd.server.SshServer;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.apache.sshd.sftp.common.SftpConstants;
|
||||
import org.apache.sshd.sftp.common.SftpException;
|
||||
import org.apache.sshd.sftp.server.SftpSubsystemFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.file.DefaultFileNameGenerator;
|
||||
import org.springframework.integration.file.remote.FileInfo;
|
||||
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
|
||||
import org.springframework.integration.file.remote.session.CachingSessionFactory;
|
||||
import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.integration.file.remote.session.SessionFactory;
|
||||
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
|
||||
@@ -70,13 +71,6 @@ import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
@@ -85,19 +79,18 @@ import com.jcraft.jsch.SftpException;
|
||||
*/
|
||||
public class SftpOutboundTests {
|
||||
|
||||
private static final com.jcraft.jsch.Session jschSession = mock(com.jcraft.jsch.Session.class);
|
||||
|
||||
@Test
|
||||
public void testHandleFileMessage() throws Exception {
|
||||
File targetDir = new File("remote-target-dir");
|
||||
assertThat(targetDir.exists()).as("target directory does not exist: " + targetDir.getName()).isTrue();
|
||||
|
||||
SessionFactory<LsEntry> sessionFactory = new TestSftpSessionFactory();
|
||||
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<>(sessionFactory);
|
||||
SessionFactory<SftpClient.DirEntry> sessionFactory = new TestSftpSessionFactory();
|
||||
FileTransferringMessageHandler<SftpClient.DirEntry> handler =
|
||||
new FileTransferringMessageHandler<>(sessionFactory);
|
||||
handler.setRemoteDirectoryExpression(new LiteralExpression(targetDir.getName()));
|
||||
DefaultFileNameGenerator fGenerator = new DefaultFileNameGenerator();
|
||||
fGenerator.setBeanFactory(mock(BeanFactory.class));
|
||||
fGenerator.setExpression("payload + '.test'");
|
||||
fGenerator.setExpression("payload.name + '.test'");
|
||||
handler.setFileNameGenerator(fGenerator);
|
||||
handler.setBeanFactory(mock(BeanFactory.class));
|
||||
handler.afterPropertiesSet();
|
||||
@@ -118,8 +111,9 @@ public class SftpOutboundTests {
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
SessionFactory<LsEntry> sessionFactory = new TestSftpSessionFactory();
|
||||
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<>(sessionFactory);
|
||||
SessionFactory<SftpClient.DirEntry> sessionFactory = new TestSftpSessionFactory();
|
||||
FileTransferringMessageHandler<SftpClient.DirEntry> handler =
|
||||
new FileTransferringMessageHandler<>(sessionFactory);
|
||||
DefaultFileNameGenerator fGenerator = new DefaultFileNameGenerator();
|
||||
fGenerator.setBeanFactory(mock(BeanFactory.class));
|
||||
fGenerator.setExpression("'foo.txt'");
|
||||
@@ -128,7 +122,7 @@ public class SftpOutboundTests {
|
||||
handler.setBeanFactory(mock(BeanFactory.class));
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
handler.handleMessage(new GenericMessage<String>("String data"));
|
||||
handler.handleMessage(new GenericMessage<>("String data"));
|
||||
assertThat(new File("remote-target-dir", "foo.txt").exists()).isTrue();
|
||||
byte[] inFile = FileCopyUtils.copyToByteArray(file);
|
||||
assertThat(new String(inFile)).isEqualTo("String data");
|
||||
@@ -141,8 +135,9 @@ public class SftpOutboundTests {
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
SessionFactory<LsEntry> sessionFactory = new TestSftpSessionFactory();
|
||||
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<>(sessionFactory);
|
||||
SessionFactory<SftpClient.DirEntry> sessionFactory = new TestSftpSessionFactory();
|
||||
FileTransferringMessageHandler<SftpClient.DirEntry> handler =
|
||||
new FileTransferringMessageHandler<>(sessionFactory);
|
||||
DefaultFileNameGenerator fGenerator = new DefaultFileNameGenerator();
|
||||
fGenerator.setBeanFactory(mock(BeanFactory.class));
|
||||
fGenerator.setExpression("'foo.txt'");
|
||||
@@ -174,7 +169,7 @@ public class SftpOutboundTests {
|
||||
|
||||
MessageChannel channel = context.getBean("outboundChannelAdapterInsideChain", MessageChannel.class);
|
||||
|
||||
channel.send(new GenericMessage<File>(srcFile));
|
||||
channel.send(new GenericMessage<>(srcFile));
|
||||
assertThat(destFile.exists()).as("destination file was not created").isTrue();
|
||||
context.close();
|
||||
}
|
||||
@@ -203,15 +198,15 @@ public class SftpOutboundTests {
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test //INT-2954
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMkDir() throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
Session<LsEntry> session = mock(Session.class);
|
||||
Session<SftpClient.DirEntry> session = mock(Session.class);
|
||||
when(session.exists(anyString())).thenReturn(Boolean.FALSE);
|
||||
@SuppressWarnings("unchecked")
|
||||
SessionFactory<LsEntry> sessionFactory = mock(SessionFactory.class);
|
||||
SessionFactory<SftpClient.DirEntry> sessionFactory = mock(SessionFactory.class);
|
||||
when(sessionFactory.getSession()).thenReturn(session);
|
||||
FileTransferringMessageHandler<LsEntry> handler = new FileTransferringMessageHandler<>(sessionFactory);
|
||||
FileTransferringMessageHandler<SftpClient.DirEntry> handler =
|
||||
new FileTransferringMessageHandler<>(sessionFactory);
|
||||
handler.setAutoCreateDirectory(true);
|
||||
handler.setRemoteDirectoryExpression(new LiteralExpression("/foo/bar/baz"));
|
||||
handler.setBeanFactory(mock(BeanFactory.class));
|
||||
@@ -228,186 +223,56 @@ public class SftpOutboundTests {
|
||||
assertThat(madeDirs.get(2)).isEqualTo("/foo/bar/baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedSession() throws Exception {
|
||||
JSch jsch = spy(new JSch());
|
||||
Constructor<com.jcraft.jsch.Session> ctor = com.jcraft.jsch.Session.class.getDeclaredConstructor(JSch.class,
|
||||
String.class, String.class, int.class);
|
||||
ctor.setAccessible(true);
|
||||
com.jcraft.jsch.Session jschSession1 = spy(ctor.newInstance(jsch, "foo", "host", 22));
|
||||
com.jcraft.jsch.Session jschSession2 = spy(ctor.newInstance(jsch, "foo", "host", 22));
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = { true, false })
|
||||
public void testSharedSession(boolean sharedSession) throws IOException {
|
||||
try (SshServer server = SshServer.setUpDefaultServer()) {
|
||||
server.setPasswordAuthenticator((arg0, arg1, arg2) -> true);
|
||||
server.setPort(0);
|
||||
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("hostkey.ser").toPath()));
|
||||
server.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
|
||||
final String pathname = System.getProperty("java.io.tmpdir") + File.separator + "sftptest" + File.separator;
|
||||
new File(pathname).mkdirs();
|
||||
server.setFileSystemFactory(new VirtualFileSystemFactory(Paths.get(pathname)));
|
||||
server.start();
|
||||
|
||||
willAnswer(invocation -> {
|
||||
new DirectFieldAccessor(jschSession1).setPropertyValue("isConnected", true);
|
||||
return null;
|
||||
})
|
||||
.given(jschSession1)
|
||||
.connect();
|
||||
DefaultSftpSessionFactory f = new DefaultSftpSessionFactory(sharedSession);
|
||||
f.setHost("localhost");
|
||||
f.setPort(server.getPort());
|
||||
f.setUser("user");
|
||||
f.setPassword("pass");
|
||||
f.setAllowUnknownKeys(true);
|
||||
|
||||
willAnswer(invocation -> {
|
||||
new DirectFieldAccessor(jschSession2).setPropertyValue("isConnected", true);
|
||||
return null;
|
||||
})
|
||||
.given(jschSession2)
|
||||
.connect();
|
||||
|
||||
when(jsch.getSession("foo", "host", 22)).thenReturn(jschSession1, jschSession2);
|
||||
final ChannelSftp channel1 = spy(new ChannelSftp());
|
||||
doReturn("channel1").when(channel1).toString();
|
||||
final ChannelSftp channel2 = spy(new ChannelSftp());
|
||||
doReturn("channel2").when(channel2).toString();
|
||||
new DirectFieldAccessor(channel1).setPropertyValue("session", jschSession1);
|
||||
new DirectFieldAccessor(channel2).setPropertyValue("session", jschSession1);
|
||||
// Can't use when(session.open()) with a spy
|
||||
final AtomicInteger n = new AtomicInteger();
|
||||
doAnswer(invocation -> n.getAndIncrement() == 0 ? channel1 : channel2).when(jschSession1).openChannel("sftp");
|
||||
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(jsch, true);
|
||||
factory.setHost("host");
|
||||
factory.setUser("foo");
|
||||
factory.setPassword("bar");
|
||||
noopConnect(channel1);
|
||||
noopConnect(channel2);
|
||||
Session<LsEntry> s1 = factory.getSession();
|
||||
Session<LsEntry> s2 = factory.getSession();
|
||||
assertThat(TestUtils.getPropertyValue(s2, "jschSession"))
|
||||
.isSameAs(TestUtils.getPropertyValue(s1, "jschSession"));
|
||||
assertThat(TestUtils.getPropertyValue(s1, "channel")).isSameAs(channel1);
|
||||
assertThat(TestUtils.getPropertyValue(s2, "channel")).isSameAs(channel2);
|
||||
Session<SftpClient.DirEntry> s1 = f.getSession();
|
||||
Session<SftpClient.DirEntry> s2 = f.getSession();
|
||||
if (sharedSession) {
|
||||
assertThat(TestUtils.getPropertyValue(s2, "sftpClient"))
|
||||
.isSameAs(TestUtils.getPropertyValue(s1, "sftpClient"));
|
||||
}
|
||||
else {
|
||||
assertThat(TestUtils.getPropertyValue(s2, "sftpClient"))
|
||||
.isNotSameAs(TestUtils.getPropertyValue(s1, "sftpClient"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotSharedSession() throws Exception {
|
||||
JSch jsch = spy(new JSch());
|
||||
Constructor<com.jcraft.jsch.Session> ctor =
|
||||
com.jcraft.jsch.Session.class.getDeclaredConstructor(JSch.class, String.class, String.class,
|
||||
int.class);
|
||||
ctor.setAccessible(true);
|
||||
com.jcraft.jsch.Session jschSession1 = spy(ctor.newInstance(jsch, "foo", "host", 22));
|
||||
com.jcraft.jsch.Session jschSession2 = spy(ctor.newInstance(jsch, "foo", "host", 22));
|
||||
new DirectFieldAccessor(jschSession1).setPropertyValue("isConnected", true);
|
||||
new DirectFieldAccessor(jschSession2).setPropertyValue("isConnected", true);
|
||||
when(jsch.getSession("foo", "host", 22)).thenReturn(jschSession1, jschSession2);
|
||||
ChannelSftp channel1 = spy(new ChannelSftp());
|
||||
ChannelSftp channel2 = spy(new ChannelSftp());
|
||||
new DirectFieldAccessor(channel1).setPropertyValue("session", jschSession1);
|
||||
new DirectFieldAccessor(channel2).setPropertyValue("session", jschSession1);
|
||||
doReturn(channel1).when(jschSession1).openChannel("sftp");
|
||||
doReturn(channel2).when(jschSession2).openChannel("sftp");
|
||||
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(jsch, false);
|
||||
factory.setHost("host");
|
||||
factory.setUser("foo");
|
||||
factory.setPassword("bar");
|
||||
noopConnect(channel1);
|
||||
noopConnect(channel2);
|
||||
Session<LsEntry> s1 = factory.getSession();
|
||||
Session<LsEntry> s2 = factory.getSession();
|
||||
assertThat(TestUtils.getPropertyValue(s2, "jschSession"))
|
||||
.isNotSameAs(TestUtils.getPropertyValue(s1, "jschSession"));
|
||||
assertThat(TestUtils.getPropertyValue(s1, "channel")).isSameAs(channel1);
|
||||
assertThat(TestUtils.getPropertyValue(s2, "channel")).isSameAs(channel2);
|
||||
}
|
||||
public void testExists() throws IOException {
|
||||
SftpClient sftpClient = mock(SftpClient.class);
|
||||
|
||||
@Test
|
||||
public void testSharedSessionCachedReset() throws Exception {
|
||||
JSch jsch = spy(new JSch());
|
||||
Constructor<com.jcraft.jsch.Session> ctor =
|
||||
com.jcraft.jsch.Session.class.getDeclaredConstructor(JSch.class, String.class, String.class,
|
||||
int.class);
|
||||
ctor.setAccessible(true);
|
||||
com.jcraft.jsch.Session jschSession1 = spy(ctor.newInstance(jsch, "foo", "host", 22));
|
||||
com.jcraft.jsch.Session jschSession2 = spy(ctor.newInstance(jsch, "foo", "host", 22));
|
||||
|
||||
willAnswer(invocation -> {
|
||||
new DirectFieldAccessor(jschSession1).setPropertyValue("isConnected", true);
|
||||
return null;
|
||||
})
|
||||
.given(jschSession1)
|
||||
.connect();
|
||||
|
||||
willAnswer(invocation -> {
|
||||
new DirectFieldAccessor(jschSession2).setPropertyValue("isConnected", true);
|
||||
return null;
|
||||
})
|
||||
.given(jschSession2)
|
||||
.connect();
|
||||
|
||||
when(jsch.getSession("foo", "host", 22)).thenReturn(jschSession1, jschSession2);
|
||||
final ChannelSftp channel1 = spy(new ChannelSftp());
|
||||
doReturn("channel1").when(channel1).toString();
|
||||
final ChannelSftp channel2 = spy(new ChannelSftp());
|
||||
doReturn("channel2").when(channel2).toString();
|
||||
final ChannelSftp channel3 = spy(new ChannelSftp());
|
||||
doReturn("channel3").when(channel3).toString();
|
||||
final ChannelSftp channel4 = spy(new ChannelSftp());
|
||||
doReturn("channel4").when(channel4).toString();
|
||||
new DirectFieldAccessor(channel1).setPropertyValue("session", jschSession1);
|
||||
new DirectFieldAccessor(channel2).setPropertyValue("session", jschSession1);
|
||||
// Can't use when(session.open()) with a spy
|
||||
final AtomicInteger n = new AtomicInteger();
|
||||
doAnswer(invocation -> n.getAndIncrement() == 0 ? channel1 : channel2).when(jschSession1).openChannel("sftp");
|
||||
doAnswer(invocation -> n.getAndIncrement() < 3 ? channel3 : channel4).when(jschSession2).openChannel("sftp");
|
||||
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(jsch, true);
|
||||
factory.setHost("host");
|
||||
factory.setUser("foo");
|
||||
factory.setPassword("bar");
|
||||
CachingSessionFactory<LsEntry> cachedFactory = new CachingSessionFactory<LsEntry>(factory);
|
||||
noopConnect(channel1);
|
||||
noopConnect(channel2);
|
||||
noopConnect(channel3);
|
||||
noopConnect(channel4);
|
||||
Session<LsEntry> s1 = cachedFactory.getSession();
|
||||
Session<LsEntry> s2 = cachedFactory.getSession();
|
||||
assertThat(TestUtils.getPropertyValue(s2, "targetSession.jschSession")).isSameAs(jschSession1);
|
||||
assertThat(TestUtils.getPropertyValue(s1, "targetSession.channel")).isSameAs(channel1);
|
||||
assertThat(TestUtils.getPropertyValue(s2, "targetSession.channel")).isSameAs(channel2);
|
||||
assertThat(TestUtils.getPropertyValue(s2, "targetSession.jschSession"))
|
||||
.isSameAs(TestUtils.getPropertyValue(s1, "targetSession.jschSession"));
|
||||
s1.close();
|
||||
Session<LsEntry> s3 = cachedFactory.getSession();
|
||||
assertThat(TestUtils.getPropertyValue(s3, "targetSession"))
|
||||
.isSameAs(TestUtils.getPropertyValue(s1, "targetSession"));
|
||||
assertThat(TestUtils.getPropertyValue(s3, "targetSession.channel")).isSameAs(channel1);
|
||||
s3.close();
|
||||
cachedFactory.resetCache();
|
||||
verify(jschSession1, never()).disconnect();
|
||||
s3 = cachedFactory.getSession();
|
||||
assertThat(TestUtils.getPropertyValue(s3, "targetSession.jschSession")).isSameAs(jschSession2);
|
||||
assertThat(TestUtils.getPropertyValue(s3, "targetSession"))
|
||||
.isNotSameAs(TestUtils.getPropertyValue(s1, "targetSession"));
|
||||
assertThat(TestUtils.getPropertyValue(s3, "targetSession.channel")).isSameAs(channel3);
|
||||
s2.close();
|
||||
verify(jschSession1).disconnect();
|
||||
s2 = cachedFactory.getSession();
|
||||
assertThat(TestUtils.getPropertyValue(s2, "targetSession.jschSession")).isSameAs(jschSession2);
|
||||
assertThat(TestUtils.getPropertyValue(s2, "targetSession"))
|
||||
.isNotSameAs(TestUtils.getPropertyValue(s3, "targetSession"));
|
||||
assertThat(TestUtils.getPropertyValue(s2, "targetSession.channel")).isSameAs(channel4);
|
||||
s2.close();
|
||||
s3.close();
|
||||
verify(jschSession2, never()).disconnect();
|
||||
cachedFactory.resetCache();
|
||||
verify(jschSession2).disconnect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExists() throws SftpException, IOException {
|
||||
ChannelSftp channelSftp = mock(ChannelSftp.class);
|
||||
|
||||
willReturn(mock(SftpATTRS.class))
|
||||
.given(channelSftp)
|
||||
willReturn(new SftpClient.Attributes())
|
||||
.given(sftpClient)
|
||||
.lstat(eq("exist"));
|
||||
|
||||
willThrow(new SftpException(ChannelSftp.SSH_FX_NO_SUCH_FILE, "Path does not exist."))
|
||||
.given(channelSftp)
|
||||
willThrow(new SftpException(SftpConstants.SSH_FX_NO_SUCH_FILE, "notExist"))
|
||||
.given(sftpClient)
|
||||
.lstat(eq("notExist"));
|
||||
|
||||
willThrow(new SftpException(ChannelSftp.SSH_FX_CONNECTION_LOST, "Connection lost."))
|
||||
.given(channelSftp)
|
||||
willThrow(new SshException(SshConstants.SSH_OPEN_CONNECT_FAILED, "Connection lost."))
|
||||
.given(sftpClient)
|
||||
.lstat(and(not(eq("exist")), not(eq("notExist"))));
|
||||
|
||||
SftpSession sftpSession = new SftpSession(mock(com.jcraft.jsch.Session.class));
|
||||
DirectFieldAccessor fieldAccessor = new DirectFieldAccessor(sftpSession);
|
||||
fieldAccessor.setPropertyValue("channel", channelSftp);
|
||||
SftpSession sftpSession = new SftpSession(sftpClient);
|
||||
|
||||
assertThat(sftpSession.exists("exist")).isTrue();
|
||||
|
||||
@@ -417,23 +282,18 @@ public class SftpOutboundTests {
|
||||
isThrownBy(() -> sftpSession.exists("foo"));
|
||||
}
|
||||
|
||||
private void noopConnect(ChannelSftp channel1) throws JSchException {
|
||||
doNothing().when(channel1).connect(5000);
|
||||
}
|
||||
|
||||
public static class TestSftpSessionFactory extends DefaultSftpSessionFactory {
|
||||
|
||||
@Override
|
||||
public SftpSession getSession() {
|
||||
try {
|
||||
ChannelSftp channel = mock(ChannelSftp.class);
|
||||
SftpClient sftpClient = mock(SftpClient.class);
|
||||
|
||||
doAnswer(invocation -> {
|
||||
File file = new File((String) invocation.getArgument(1));
|
||||
File file = new File((String) invocation.getArgument(0));
|
||||
assertThat(file.getName()).endsWith(".writing");
|
||||
FileCopyUtils.copy((InputStream) invocation.getArgument(0), new FileOutputStream(file));
|
||||
return null;
|
||||
}).when(channel).put(Mockito.any(InputStream.class), Mockito.anyString());
|
||||
return new FileOutputStream(file);
|
||||
}).when(sftpClient).write(Mockito.anyString());
|
||||
|
||||
doAnswer(invocation -> {
|
||||
File file = new File((String) invocation.getArgument(0));
|
||||
@@ -441,21 +301,16 @@ public class SftpOutboundTests {
|
||||
File renameToFile = new File((String) invocation.getArgument(1));
|
||||
file.renameTo(renameToFile);
|
||||
return null;
|
||||
}).when(channel).rename(Mockito.anyString(), Mockito.anyString());
|
||||
}).when(sftpClient).rename(Mockito.anyString(), Mockito.anyString(), eq(SftpClient.CopyMode.Overwrite));
|
||||
|
||||
String[] files = new File("remote-test-dir").list();
|
||||
Vector<LsEntry> sftpEntries = new Vector<>();
|
||||
for (String fileName : files) {
|
||||
LsEntry lsEntry = mock(LsEntry.class);
|
||||
SftpATTRS attributes = mock(SftpATTRS.class);
|
||||
when(lsEntry.getAttrs()).thenReturn(attributes);
|
||||
when(lsEntry.getFilename()).thenReturn(fileName);
|
||||
sftpEntries.add(lsEntry);
|
||||
}
|
||||
when(channel.ls("remote-test-dir/")).thenReturn(sftpEntries);
|
||||
List<SftpClient.DirEntry> dirEntries =
|
||||
Arrays.stream(files)
|
||||
.map((file) -> new SftpClient.DirEntry(file, file, new SftpClient.Attributes()))
|
||||
.toList();
|
||||
when(sftpClient.readDir("remote-test-dir")).thenReturn(dirEntries);
|
||||
|
||||
when(jschSession.openChannel("sftp")).thenReturn(channel);
|
||||
return SftpTestSessionFactory.createSftpSession(jschSession);
|
||||
return SftpTestSessionFactory.createSftpSession(sftpClient);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException("Failed to create mock sftp session", e);
|
||||
|
||||
@@ -19,6 +19,10 @@ package org.springframework.integration.sftp.outbound;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -40,6 +44,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -78,9 +83,6 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
@@ -128,7 +130,7 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
private DirectChannel inboundMPutRecursiveFiltered;
|
||||
|
||||
@Autowired
|
||||
private SessionFactory<LsEntry> sessionFactory;
|
||||
private SessionFactory<SftpClient.DirEntry> sessionFactory;
|
||||
|
||||
@Autowired
|
||||
private DirectChannel appending;
|
||||
@@ -180,8 +182,6 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
assertThat(localFile.getPath().replaceAll(Matcher.quoteReplacement(File.separator), "/"))
|
||||
.contains(dir.toUpperCase());
|
||||
Session<?> session2 = this.sessionFactory.getSession();
|
||||
assertThat(TestUtils.getPropertyValue(session2, "targetSession.jschSession"))
|
||||
.isSameAs(TestUtils.getPropertyValue(session, "targetSession.jschSession"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -284,10 +284,10 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
assertThat(files.stream()
|
||||
.map(fi -> fi.getFilename())
|
||||
.collect(Collectors.toList())).contains(
|
||||
" sftpSource1.txt",
|
||||
"sftpSource2.txt",
|
||||
"subSftpSource",
|
||||
"subSftpSource/subSftpSource1.txt");
|
||||
" sftpSource1.txt",
|
||||
"sftpSource2.txt",
|
||||
"subSftpSource",
|
||||
"subSftpSource/subSftpSource1.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -302,14 +302,14 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
assertThat(files.stream()
|
||||
.map(fi -> fi.getFilename())
|
||||
.collect(Collectors.toList())).contains(
|
||||
" sftpSource1.txt",
|
||||
"sftpSource2.txt",
|
||||
"subSftpSource",
|
||||
"subSftpSource/subSftpSource1.txt",
|
||||
".",
|
||||
"..",
|
||||
"subSftpSource/.",
|
||||
"subSftpSource/..");
|
||||
" sftpSource1.txt",
|
||||
"sftpSource2.txt",
|
||||
"subSftpSource",
|
||||
"subSftpSource/subSftpSource1.txt",
|
||||
".",
|
||||
"..",
|
||||
"subSftpSource/.",
|
||||
"subSftpSource/..");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -324,9 +324,9 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
assertThat(files.stream()
|
||||
.map(fi -> fi.getFilename())
|
||||
.collect(Collectors.toList())).contains(
|
||||
" sftpSource1.txt",
|
||||
"sftpSource2.txt",
|
||||
"subSftpSource/subSftpSource1.txt");
|
||||
" sftpSource1.txt",
|
||||
"sftpSource2.txt",
|
||||
"subSftpSource/subSftpSource1.txt");
|
||||
File newDeepFile = new File(this.sourceRemoteDirectory + "/subSftpSource/subSftpSource2.txt");
|
||||
OutputStream fos = new FileOutputStream(newDeepFile);
|
||||
fos.write("test".getBytes());
|
||||
@@ -474,8 +474,9 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
Session<?> session = sessionFactory.getSession();
|
||||
session.close();
|
||||
session = TestUtils.getPropertyValue(session, "targetSession", Session.class);
|
||||
ChannelSftp channel = spy(TestUtils.getPropertyValue(session, "channel", ChannelSftp.class));
|
||||
new DirectFieldAccessor(session).setPropertyValue("channel", channel);
|
||||
SftpClient sftpClient = spy(TestUtils.getPropertyValue(session, "sftpClient", SftpClient.class));
|
||||
doNothing().when(sftpClient).setStat(anyString(), any(SftpClient.Attributes.class));
|
||||
new DirectFieldAccessor(session).setPropertyValue("sftpClient", sftpClient);
|
||||
|
||||
String dir = "sftpSource/";
|
||||
this.inboundMGetRecursive.send(new GenericMessage<Object>(dir + "*"));
|
||||
@@ -492,8 +493,8 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
.isIn("sftpTarget/localSource1.txt", "sftpTarget/localSource2.txt");
|
||||
assertThat(out.getPayload().get(1))
|
||||
.isIn("sftpTarget/localSource1.txt", "sftpTarget/localSource2.txt");
|
||||
verify(channel).chmod(0600, "sftpTarget/localSource1.txt");
|
||||
verify(channel).chmod(0600, "sftpTarget/localSource2.txt");
|
||||
verify(sftpClient).setStat(eq("sftpTarget/localSource1.txt"), any(SftpClient.Attributes.class));
|
||||
verify(sftpClient).setStat(eq("sftpTarget/localSource2.txt"), any(SftpClient.Attributes.class));
|
||||
resetSessionCache();
|
||||
assertThat(this.config.latch.await(10, TimeUnit.SECONDS)).isTrue();
|
||||
assertThat(this.config.events).hasSize(6);
|
||||
@@ -676,9 +677,10 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
}
|
||||
|
||||
private void assertLength6(SftpRemoteFileTemplate template) {
|
||||
LsEntry[] files = template.execute(session -> session.list("sftpTarget/appending.txt"));
|
||||
assertThat(files.length).isEqualTo(1);
|
||||
assertThat(files[0].getAttrs().getSize()).isEqualTo(6);
|
||||
SftpClient.DirEntry[] files = template.execute(session -> session.list("sftpTarget"));
|
||||
assertThat(files.length).isEqualTo(3);
|
||||
assertThat(files[2].getFilename()).isEqualTo("appending.txt");
|
||||
assertThat(files[2].getAttributes().getSize()).isEqualTo(6);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -689,7 +691,7 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
sessionFactory.setUser("foo");
|
||||
sessionFactory.setPassword("foo");
|
||||
sessionFactory.setAllowUnknownKeys(true);
|
||||
Session<LsEntry> session = sessionFactory.getSession();
|
||||
Session<SftpClient.DirEntry> session = sessionFactory.getSession();
|
||||
|
||||
assertThat(session.exists("sftpSource")).isTrue();
|
||||
assertThat(session.exists("notExist")).isFalse();
|
||||
@@ -699,15 +701,15 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
assertThatExceptionOfType(UncheckedIOException.class)
|
||||
.isThrownBy(() -> session.exists("any"))
|
||||
.withRootCauseInstanceOf(IOException.class)
|
||||
.withStackTraceContaining("Pipe closed");
|
||||
.withStackTraceContaining("lstat(any) client is closed");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final class TestMessageSessionCallback
|
||||
implements MessageSessionCallback<LsEntry, Object> {
|
||||
implements MessageSessionCallback<SftpClient.DirEntry, Object> {
|
||||
|
||||
@Override
|
||||
public Object doInSession(Session<ChannelSftp.LsEntry> session, Message<?> requestMessage) {
|
||||
public Object doInSession(Session<SftpClient.DirEntry> session, Message<?> requestMessage) {
|
||||
return ((String) requestMessage.getPayload()).toUpperCase();
|
||||
}
|
||||
|
||||
@@ -722,13 +724,13 @@ public class SftpServerOutboundTests extends SftpTestSupport {
|
||||
private volatile CountDownLatch latch;
|
||||
|
||||
@Bean
|
||||
public SessionFactory<LsEntry> sftpSessionFactory(ApplicationContext context) {
|
||||
public SessionFactory<SftpClient.DirEntry> sftpSessionFactory(ApplicationContext context) {
|
||||
SftpServerOutboundTests.eventListener().setApplicationEventPublisher(context);
|
||||
return SftpServerOutboundTests.sessionFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SftpRemoteFileTemplate template(SessionFactory<LsEntry> sf) {
|
||||
public SftpRemoteFileTemplate template(SessionFactory<SftpClient.DirEntry> sf) {
|
||||
return new SftpRemoteFileTemplate(sf);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-2020 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.integration.sftp.session;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
|
||||
import com.jcraft.jsch.Proxy;
|
||||
import com.jcraft.jsch.ProxyHTTP;
|
||||
import com.jcraft.jsch.ProxySOCKS4;
|
||||
import com.jcraft.jsch.ProxySOCKS5;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 4.3
|
||||
*
|
||||
*/
|
||||
public class ProxyTests {
|
||||
|
||||
@Test
|
||||
@Disabled // TODO Use SftpTestSupport
|
||||
/*
|
||||
* Needs host and account
|
||||
*/
|
||||
public void testSimpleConnect() {
|
||||
DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
|
||||
sf.setHost("10.0.0.3");
|
||||
sf.setPort(22);
|
||||
sf.setUser("ftptest");
|
||||
sf.setPassword("ftptest");
|
||||
sf.setAllowUnknownKeys(true);
|
||||
sf.getSession().close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
/*
|
||||
* Needs host and account and...
|
||||
* $ ssh -D 1080 -f -N gpr@10.0.0.3
|
||||
*/
|
||||
public void testProxyConnect() throws Exception {
|
||||
DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
|
||||
JschProxyFactoryBean proxyFactoryBean = new JschProxyFactoryBean(JschProxyFactoryBean.Type.SOCKS5, "localhost",
|
||||
1080, "ftptest", "ftptest");
|
||||
proxyFactoryBean.afterPropertiesSet();
|
||||
sf.setHost("10.0.0.3");
|
||||
sf.setPort(22);
|
||||
sf.setUser("ftptest");
|
||||
sf.setPassword("ftptest");
|
||||
sf.setProxy(proxyFactoryBean.getObject());
|
||||
sf.setAllowUnknownKeys(true);
|
||||
sf.getSession().close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryBean() throws Exception {
|
||||
JschProxyFactoryBean proxyFactoryBean = new JschProxyFactoryBean(JschProxyFactoryBean.Type.SOCKS5, "localhost",
|
||||
1080, "ftptest", "pass");
|
||||
proxyFactoryBean.afterPropertiesSet();
|
||||
Proxy proxy = proxyFactoryBean.getObject();
|
||||
assertProxy(proxy, ProxySOCKS5.class);
|
||||
|
||||
proxyFactoryBean = new JschProxyFactoryBean(JschProxyFactoryBean.Type.SOCKS4, "localhost",
|
||||
1080, "ftptest", "pass");
|
||||
proxyFactoryBean.afterPropertiesSet();
|
||||
proxy = proxyFactoryBean.getObject();
|
||||
assertProxy(proxy, ProxySOCKS4.class);
|
||||
|
||||
proxyFactoryBean = new JschProxyFactoryBean(JschProxyFactoryBean.Type.HTTP, "localhost",
|
||||
1080, "ftptest", "pass");
|
||||
proxyFactoryBean.afterPropertiesSet();
|
||||
proxy = proxyFactoryBean.getObject();
|
||||
assertProxy(proxy, ProxyHTTP.class);
|
||||
}
|
||||
|
||||
private void assertProxy(Proxy proxy, Class<? extends Proxy> clazz) {
|
||||
assertThat(proxy).isInstanceOf(clazz);
|
||||
assertThat(TestUtils.getPropertyValue(proxy, "user")).isEqualTo("ftptest");
|
||||
assertThat(TestUtils.getPropertyValue(proxy, "passwd")).isEqualTo("pass");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.integration.sftp.session;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.sshd.client.ClientFactoryManager;
|
||||
import org.apache.sshd.client.config.hosts.HostPatternsHolder;
|
||||
import org.apache.sshd.client.config.hosts.KnownHostEntry;
|
||||
import org.apache.sshd.client.session.ClientSession;
|
||||
import org.apache.sshd.common.Factory;
|
||||
import org.apache.sshd.common.config.keys.AuthorizedKeyEntry;
|
||||
import org.apache.sshd.common.config.keys.PublicKeyEntryResolver;
|
||||
import org.apache.sshd.common.random.JceRandomFactory;
|
||||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.net.SshdSocketAddress;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIf;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
@EnabledIf("isDefaultKnownHostsFilePresent")
|
||||
public class ResourceKnownHostsServerKeyVerifierTests {
|
||||
|
||||
private static final Map<SshdSocketAddress, PublicKey> HOST_KEYS = new TreeMap<>(SshdSocketAddress.BY_HOST_AND_PORT);
|
||||
|
||||
@BeforeAll
|
||||
static void loadHostKeys() throws GeneralSecurityException, IOException {
|
||||
Map<SshdSocketAddress, KnownHostEntry> hostsEntries = loadEntries(KnownHostEntry.getDefaultKnownHostsFile());
|
||||
for (Map.Entry<SshdSocketAddress, KnownHostEntry> ke : hostsEntries.entrySet()) {
|
||||
SshdSocketAddress hostIdentity = ke.getKey();
|
||||
KnownHostEntry entry = ke.getValue();
|
||||
AuthorizedKeyEntry authEntry = entry.getKeyEntry();
|
||||
PublicKey key = authEntry.resolvePublicKey(null, Collections.emptyMap(), PublicKeyEntryResolver.FAILING);
|
||||
HOST_KEYS.put(hostIdentity, key);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
void testServerKeys() {
|
||||
ResourceKnownHostsServerKeyVerifier verifier
|
||||
= new ResourceKnownHostsServerKeyVerifier(
|
||||
new FileSystemResource(KnownHostEntry.getDefaultKnownHostsFile()));
|
||||
|
||||
ClientFactoryManager manager = Mockito.mock(ClientFactoryManager.class);
|
||||
Mockito.when(manager.getRandomFactory()).thenReturn((Factory) JceRandomFactory.INSTANCE);
|
||||
|
||||
HOST_KEYS.forEach((key, value) -> {
|
||||
ClientSession session = Mockito.mock(ClientSession.class);
|
||||
Mockito.when(session.getFactoryManager()).thenReturn(manager);
|
||||
|
||||
Mockito.when(session.getConnectAddress()).thenReturn(key);
|
||||
assertThat(verifier.verifyServerKey(session, key, value)).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
private static Map<SshdSocketAddress, KnownHostEntry> loadEntries(Path file) throws IOException {
|
||||
Collection<KnownHostEntry> entries = KnownHostEntry.readKnownHostEntries(file);
|
||||
if (GenericUtils.isEmpty(entries)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<SshdSocketAddress, KnownHostEntry> hostsMap = new TreeMap<>(SshdSocketAddress.BY_HOST_AND_PORT);
|
||||
for (KnownHostEntry entry : entries) {
|
||||
String line = entry.getConfigLine();
|
||||
// extract hosts
|
||||
int pos = line.indexOf(' ');
|
||||
String patterns = line.substring(0, pos);
|
||||
if (entry.getHashedEntry() != null) {
|
||||
hostsMap.put(new SshdSocketAddress("localhost", 0), entry);
|
||||
}
|
||||
else {
|
||||
String[] addrs = GenericUtils.split(patterns, ',');
|
||||
for (String a : addrs) {
|
||||
int port = 0;
|
||||
if (a.charAt(0) == HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_START_DELIM) {
|
||||
pos = a.indexOf(HostPatternsHolder.NON_STANDARD_PORT_PATTERN_ENCLOSURE_END_DELIM, 1);
|
||||
|
||||
port = Integer.parseInt(a.substring(pos + 2));
|
||||
a = a.substring(1, pos);
|
||||
}
|
||||
hostsMap.put(new SshdSocketAddress(a, port), entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hostsMap;
|
||||
}
|
||||
|
||||
static boolean isDefaultKnownHostsFilePresent() {
|
||||
return KnownHostEntry.getDefaultKnownHostsFile().toFile().exists();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2020 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,10 +20,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
@@ -44,11 +45,6 @@ import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import com.jcraft.jsch.SftpATTRS;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
@@ -61,7 +57,7 @@ import com.jcraft.jsch.SftpException;
|
||||
public class SftpRemoteFileTemplateTests extends SftpTestSupport {
|
||||
|
||||
@Autowired
|
||||
private CachingSessionFactory<LsEntry> sessionFactory;
|
||||
private CachingSessionFactory<SftpClient.DirEntry> sessionFactory;
|
||||
|
||||
@Test
|
||||
public void testINT3412AppendStatRmdir() {
|
||||
@@ -82,24 +78,24 @@ public class SftpRemoteFileTemplateTests extends SftpTestSupport {
|
||||
template.append(new GenericMessage<>("foo"));
|
||||
template.append(new GenericMessage<>("bar"));
|
||||
assertThat(template.exists("foo/foobar.txt")).isTrue();
|
||||
template.executeWithClient((ClientCallbackWithoutResult<ChannelSftp>) client -> {
|
||||
template.executeWithClient((ClientCallbackWithoutResult<SftpClient>) client -> {
|
||||
try {
|
||||
SftpATTRS file = client.lstat("foo/foobar.txt");
|
||||
SftpClient.Attributes file = client.lstat("foo/foobar.txt");
|
||||
assertThat(file.getSize()).isEqualTo(6);
|
||||
}
|
||||
catch (SftpException e) {
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
template.execute((SessionCallbackWithoutResult<LsEntry>) session -> {
|
||||
LsEntry[] files = session.list("foo/");
|
||||
template.execute((SessionCallbackWithoutResult<SftpClient.DirEntry>) session -> {
|
||||
SftpClient.DirEntry[] files = session.list("foo/");
|
||||
assertThat(files.length).isEqualTo(4);
|
||||
assertThat(session.remove("foo/foobar.txt")).isTrue();
|
||||
assertThat(session.rmdir("foo/bar/")).isTrue();
|
||||
files = session.list("foo/");
|
||||
assertThat(files.length).isEqualTo(2);
|
||||
List<LsEntry> list = Arrays.asList(files);
|
||||
assertThat(list.stream().map(l -> l.getFilename()).collect(Collectors.toList())).contains(".", "..");
|
||||
List<String> fileNames = Arrays.stream(files).map(SftpClient.DirEntry::getFilename).toList();
|
||||
assertThat(fileNames).contains(".", "..");
|
||||
assertThat(session.rmdir("foo/")).isTrue();
|
||||
});
|
||||
assertThat(template.exists("foo")).isFalse();
|
||||
@@ -107,8 +103,8 @@ public class SftpRemoteFileTemplateTests extends SftpTestSupport {
|
||||
|
||||
@Test
|
||||
public void testNoDeadLockOnSend() {
|
||||
CachingSessionFactory<LsEntry> cachingSessionFactory = new CachingSessionFactory<>(sessionFactory(), 1);
|
||||
SftpRemoteFileTemplate template = new SftpRemoteFileTemplate(cachingSessionFactory);
|
||||
CachingSessionFactory<SftpClient.DirEntry> sessionFactory = new CachingSessionFactory<>(sessionFactory(), 1);
|
||||
SftpRemoteFileTemplate template = new SftpRemoteFileTemplate(sessionFactory);
|
||||
template.setRemoteDirectoryExpression(new LiteralExpression(""));
|
||||
template.setBeanFactory(mock(BeanFactory.class));
|
||||
template.setUseTemporaryFileName(false);
|
||||
@@ -125,14 +121,14 @@ public class SftpRemoteFileTemplateTests extends SftpTestSupport {
|
||||
.withCauseInstanceOf(MessagingException.class)
|
||||
.withStackTraceContaining("he destination file already exists at 'test.file'.");
|
||||
|
||||
cachingSessionFactory.destroy();
|
||||
sessionFactory.destroy();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
public SessionFactory<LsEntry> ftpSessionFactory() {
|
||||
public SessionFactory<SftpClient.DirEntry> ftpSessionFactory() {
|
||||
return SftpRemoteFileTemplateTests.sessionFactory();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,6 +35,7 @@ import java.util.Collections;
|
||||
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
|
||||
import org.apache.sshd.server.SshServer;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
import org.apache.sshd.sftp.server.SftpSubsystemFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -44,8 +45,6 @@ import org.springframework.integration.file.remote.session.Session;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
* *
|
||||
* @author Gary Russell
|
||||
@@ -59,8 +58,7 @@ public class SftpServerTests {
|
||||
|
||||
@Test
|
||||
public void testUcPw() throws Exception {
|
||||
SshServer server = SshServer.setUpDefaultServer();
|
||||
try {
|
||||
try (SshServer server = SshServer.setUpDefaultServer()) {
|
||||
server.setPasswordAuthenticator((arg0, arg1, arg2) -> true);
|
||||
server.setPort(0);
|
||||
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("hostkey.ser").toPath()));
|
||||
@@ -76,12 +74,9 @@ public class SftpServerTests {
|
||||
f.setUser("user");
|
||||
f.setPassword("pass");
|
||||
f.setAllowUnknownKeys(true);
|
||||
Session<LsEntry> session = f.getSession();
|
||||
Session<SftpClient.DirEntry> session = f.getSession();
|
||||
doTest(server, session);
|
||||
}
|
||||
finally {
|
||||
server.stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,11 +89,9 @@ public class SftpServerTests {
|
||||
testKeyExchange("id_rsa_pp.pub", "id_rsa_pp", "secret");
|
||||
}
|
||||
|
||||
private void testKeyExchange(String pubKey, String privKey, String passphrase)
|
||||
throws Exception {
|
||||
SshServer server = SshServer.setUpDefaultServer();
|
||||
private void testKeyExchange(String pubKey, String privKey, String passphrase) throws Exception {
|
||||
final PublicKey allowedKey = decodePublicKey(pubKey);
|
||||
try {
|
||||
try (SshServer server = SshServer.setUpDefaultServer()) {
|
||||
server.setPublickeyAuthenticator((username, key, session) -> key.equals(allowedKey));
|
||||
server.setPort(0);
|
||||
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("hostkey.ser").toPath()));
|
||||
@@ -116,12 +109,9 @@ public class SftpServerTests {
|
||||
InputStream stream = new ClassPathResource(privKey).getInputStream();
|
||||
f.setPrivateKey(new ByteArrayResource(FileCopyUtils.copyToByteArray(stream)));
|
||||
f.setPrivateKeyPassphrase(passphrase);
|
||||
Session<LsEntry> session = f.getSession();
|
||||
Session<SftpClient.DirEntry> session = f.getSession();
|
||||
doTest(server, session);
|
||||
}
|
||||
finally {
|
||||
server.stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
private PublicKey decodePublicKey(String key) throws Exception {
|
||||
@@ -155,12 +145,15 @@ public class SftpServerTests {
|
||||
return new BigInteger(bytes);
|
||||
}
|
||||
|
||||
protected void doTest(SshServer server, Session<LsEntry> session) throws IOException {
|
||||
protected void doTest(SshServer server, Session<SftpClient.DirEntry> session) throws IOException {
|
||||
assertThat(server.getActiveSessions().size()).isEqualTo(1);
|
||||
LsEntry[] list = session.list(".");
|
||||
if (list.length > 0) {
|
||||
session.remove("*");
|
||||
SftpClient.DirEntry[] list = session.list(".");
|
||||
for (SftpClient.DirEntry entry : list) {
|
||||
if (entry.getAttributes().isRegularFile()) {
|
||||
session.remove(entry.getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
session.write(new ByteArrayInputStream("foo".getBytes()), "bar");
|
||||
list = session.list(".");
|
||||
assertThat(list[1].getFilename()).isEqualTo("bar");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2021 the original author or authors.
|
||||
* Copyright 2014-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,28 +18,15 @@ package org.springframework.integration.sftp.session;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.apache.sshd.common.SshException;
|
||||
import org.apache.sshd.server.SshServer;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.sftp.server.SftpSubsystemFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import com.jcraft.jsch.UserInfo;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
@@ -54,8 +41,7 @@ public class SftpSessionFactoryTests {
|
||||
*/
|
||||
@Test
|
||||
public void testConnectFailSocketOpen() throws Exception {
|
||||
SshServer server = SshServer.setUpDefaultServer();
|
||||
try {
|
||||
try (SshServer server = SshServer.setUpDefaultServer()) {
|
||||
server.setPasswordAuthenticator((arg0, arg1, arg2) -> true);
|
||||
server.setPort(0);
|
||||
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("hostkey.ser").toPath()));
|
||||
@@ -69,24 +55,22 @@ public class SftpSessionFactoryTests {
|
||||
int n = 0;
|
||||
while (true) {
|
||||
try {
|
||||
f.getSession();
|
||||
f.getSession().connect();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (e instanceof IllegalStateException && "failed to create SFTP Session".equals(e.getMessage())) {
|
||||
if (e.getCause() instanceof IllegalStateException) {
|
||||
if (e.getCause().getCause() instanceof JSchException) {
|
||||
if (e.getCause().getCause().getCause() instanceof ConnectException) {
|
||||
assertThat(n++ < 100).as("Server failed to start in 10 seconds").isTrue();
|
||||
Thread.sleep(100);
|
||||
continue;
|
||||
}
|
||||
if (e.getCause().getCause() instanceof ConnectException) {
|
||||
assertThat(n++ < 100).as("Server failed to start in 10 seconds").isTrue();
|
||||
Thread.sleep(100);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(e).isInstanceOf(IllegalStateException.class);
|
||||
assertThat(e.getCause()).isInstanceOf(IllegalStateException.class);
|
||||
assertThat(e.getCause().getMessage()).isEqualTo("failed to connect");
|
||||
assertThat(e.getCause()).isInstanceOf(SshException.class);
|
||||
assertThat(e.getCause().getMessage()).isEqualTo("Server key did not validate");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -98,130 +82,6 @@ public class SftpSessionFactoryTests {
|
||||
|
||||
assertThat(server.getActiveSessions().size()).isEqualTo(0);
|
||||
}
|
||||
finally {
|
||||
server.stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPasswordPassPhraseViaUserInfo() {
|
||||
DefaultSftpSessionFactory f = new DefaultSftpSessionFactory();
|
||||
f.setUser("user");
|
||||
f.setAllowUnknownKeys(true);
|
||||
UserInfo ui = mock(UserInfo.class);
|
||||
when(ui.getPassword()).thenReturn("pass");
|
||||
when(ui.getPassphrase()).thenReturn("pp").thenReturn(null);
|
||||
f.setUserInfo(ui);
|
||||
UserInfo userInfo = TestUtils.getPropertyValue(f, "userInfoWrapper", UserInfo.class);
|
||||
assertThat(userInfo.getPassword()).isEqualTo("pass");
|
||||
f.setPassword("foo");
|
||||
try {
|
||||
userInfo.getPassword();
|
||||
fail("expected Exception");
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
assertThat(e.getMessage()).startsWith("When a 'UserInfo' is provided, 'password' is not allowed");
|
||||
}
|
||||
assertThat(userInfo.getPassphrase()).isEqualTo("pp");
|
||||
f.setPrivateKeyPassphrase("bar");
|
||||
try {
|
||||
userInfo.getPassphrase();
|
||||
fail("expected Exception");
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
assertThat(e
|
||||
.getMessage()).startsWith("When a 'UserInfo' is provided, 'privateKeyPassphrase' is not allowed");
|
||||
}
|
||||
f.setUserInfo(null);
|
||||
assertThat(userInfo.getPassword()).isEqualTo("foo");
|
||||
assertThat(userInfo.getPassphrase()).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultUserInfoFalse() throws Exception {
|
||||
SshServer server = SshServer.setUpDefaultServer();
|
||||
try {
|
||||
DefaultSftpSessionFactory f = createServerAndClient(server);
|
||||
expectReject(f);
|
||||
}
|
||||
finally {
|
||||
server.stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultUserInfoTrue() throws Exception {
|
||||
SshServer server = SshServer.setUpDefaultServer();
|
||||
try {
|
||||
DefaultSftpSessionFactory f = createServerAndClient(server);
|
||||
f.setChannelConnectTimeout(Duration.ofSeconds(6));
|
||||
f.setAllowUnknownKeys(true);
|
||||
SftpSession session = f.getSession();
|
||||
assertThat(TestUtils.getPropertyValue(session, "channelConnectTimeout", Integer.class)).isEqualTo(6_000);
|
||||
session.close();
|
||||
}
|
||||
finally {
|
||||
server.stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomUserInfoFalse() throws Exception {
|
||||
SshServer server = SshServer.setUpDefaultServer();
|
||||
try {
|
||||
DefaultSftpSessionFactory f = createServerAndClient(server);
|
||||
UserInfo userInfo = mock(UserInfo.class);
|
||||
when(userInfo.promptYesNo(anyString())).thenReturn(false);
|
||||
f.setUserInfo(userInfo);
|
||||
expectReject(f);
|
||||
}
|
||||
finally {
|
||||
server.stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void expectReject(DefaultSftpSessionFactory f) {
|
||||
try {
|
||||
f.getSession().close();
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e).isInstanceOf(IllegalStateException.class);
|
||||
assertThat(e.getCause()).isInstanceOf(IllegalStateException.class);
|
||||
assertThat(e.getCause().getCause()).isInstanceOf(JSchException.class);
|
||||
assertThat(e.getCause().getCause().getMessage()).contains("reject HostKey");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomUserInfoTrue() throws Exception {
|
||||
SshServer server = SshServer.setUpDefaultServer();
|
||||
try {
|
||||
DefaultSftpSessionFactory f = createServerAndClient(server);
|
||||
UserInfo userInfo = mock(UserInfo.class);
|
||||
when(userInfo.promptYesNo(anyString())).thenReturn(true);
|
||||
f.setUserInfo(userInfo);
|
||||
f.getSession().close();
|
||||
}
|
||||
finally {
|
||||
server.stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
private DefaultSftpSessionFactory createServerAndClient(SshServer server) throws IOException {
|
||||
server.setPublickeyAuthenticator((username, key, session) -> true);
|
||||
server.setPort(0);
|
||||
server.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
|
||||
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("hostkey.ser").toPath()));
|
||||
server.start();
|
||||
|
||||
DefaultSftpSessionFactory f = new DefaultSftpSessionFactory();
|
||||
f.setHost("localhost");
|
||||
f.setPort(server.getPort());
|
||||
f.setUser("user");
|
||||
Resource privateKey = new ClassPathResource("id_rsa");
|
||||
f.setPrivateKey(privateKey);
|
||||
return f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,11 @@
|
||||
package org.springframework.integration.sftp.session;
|
||||
|
||||
|
||||
import static org.mockito.BDDMockito.willReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import org.apache.sshd.sftp.client.SftpClient;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
@@ -28,9 +33,12 @@ public class SftpTestSessionFactory {
|
||||
super();
|
||||
}
|
||||
|
||||
public static SftpSession createSftpSession(com.jcraft.jsch.Session jschSession) {
|
||||
SftpSession sftpSession = new SftpSession(jschSession);
|
||||
sftpSession.connect();
|
||||
public static SftpSession createSftpSession(SftpClient sftpClient) {
|
||||
SftpSession sftpSession = spy(new SftpSession(sftpClient));
|
||||
willReturn("mock.sftp.host:22")
|
||||
.given(sftpSession)
|
||||
.getHostPort();
|
||||
return sftpSession;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="com.jcraft.jsch" level="warn"/>
|
||||
<Logger name="org.apache.sshd" level="error"/>
|
||||
<Logger name="org.springframework.integration" level="warn"/>
|
||||
<Logger name="org.springframework.integration.sftp" level="warn"/>
|
||||
<Root level="warn">
|
||||
|
||||
Reference in New Issue
Block a user