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:
Artem Bilan
2022-09-27 13:10:11 -04:00
committed by GitHub
parent 69176632c0
commit 4aa2f91bd9
61 changed files with 1048 additions and 2232 deletions

View File

@@ -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() {

View File

@@ -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">

View File

@@ -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);
}

View File

@@ -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>

View File

@@ -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();

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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());
}

View File

@@ -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");

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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");
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}

View File

@@ -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");

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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">