Skip to content

Commit 24bb077

Browse files
authored
BAEL-7664: SFTP with JSch (#18909)
1 parent ce01a52 commit 24bb077

File tree

9 files changed

+549
-2
lines changed

9 files changed

+549
-2
lines changed

libraries-io/pom.xml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@
6363
<artifactId>jakarta.mail-api</artifactId>
6464
<version>${jakarta-mail.version}</version>
6565
</dependency>
66+
67+
<dependency>
68+
<groupId>org.testcontainers</groupId>
69+
<artifactId>testcontainers</artifactId>
70+
<version>${testcontainers.version}</version>
71+
<scope>test</scope>
72+
</dependency>
73+
<dependency>
74+
<groupId>org.testcontainers</groupId>
75+
<artifactId>testcontainers-junit-jupiter</artifactId>
76+
<version>${testcontainers.version}</version>
77+
<scope>test</scope>
78+
</dependency>
6679
</dependencies>
6780

6881
<build>
@@ -80,15 +93,16 @@
8093

8194
<properties>
8295
<!-- sftp -->
83-
<jsch.version>0.2.16</jsch.version>
96+
<jsch.version>2.27.5</jsch.version>
8497
<sshj.version>0.38.0</sshj.version>
85-
<commons-vfs2.version>2.9.0</commons-vfs2.version>
98+
<commons-vfs2.version>2.10.0</commons-vfs2.version>
8699
<zip4j.version>2.11.5</zip4j.version>
87100
<opencsv.version>5.9</opencsv.version>
88101
<spring.version>6.1.4</spring.version>
89102
<simplejavamail.version>8.7.0</simplejavamail.version>
90103
<jakarta-mail.version>2.1.3</jakarta-mail.version>
91104
<imap.version>2.0.1</imap.version>
105+
<testcontainers.version>2.0.1</testcontainers.version>
92106
</properties>
93107

94108
</project>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.baeldung.java.io.jsch;
2+
3+
import com.jcraft.jsch.UserInfo;
4+
5+
public class BaseUserInfo implements UserInfo {
6+
@Override
7+
public String getPassphrase() {
8+
return null;
9+
}
10+
11+
@Override
12+
public String getPassword() {
13+
return null;
14+
}
15+
16+
@Override
17+
public boolean promptPassword(String message) {
18+
return false;
19+
}
20+
21+
@Override
22+
public boolean promptPassphrase(String message) {
23+
return false;
24+
}
25+
26+
@Override
27+
public boolean promptYesNo(String message) {
28+
return false;
29+
}
30+
31+
@Override
32+
public void showMessage(String message) {
33+
34+
}
35+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package com.baeldung.java.io.jsch;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import java.nio.file.Paths;
7+
8+
import org.junit.jupiter.api.Test;
9+
import org.testcontainers.containers.GenericContainer;
10+
import org.testcontainers.junit.jupiter.Container;
11+
import org.testcontainers.junit.jupiter.Testcontainers;
12+
import org.testcontainers.utility.MountableFile;
13+
14+
import com.jcraft.jsch.JSch;
15+
import com.jcraft.jsch.JSchException;
16+
import com.jcraft.jsch.JSchUnknownHostKeyException;
17+
import com.jcraft.jsch.Session;
18+
19+
@Testcontainers
20+
class ConnectIntegrationTest {
21+
private static final int SFTP_PORT = 22;
22+
23+
private static final String USERNAME = "baeldung";
24+
private static final String PASSWORD = "test";
25+
private static final String DIRECTORY = "upload";
26+
27+
// atmoz/sftp is a Docker container for an SFTP server that can easily support password and key authentication.
28+
@Container
29+
public static GenericContainer<?> sftpContainer = new GenericContainer<>("atmoz/sftp:latest")
30+
.withExposedPorts(SFTP_PORT)
31+
.withCommand(USERNAME + ":" + PASSWORD + ":::" + DIRECTORY)
32+
.withCopyFileToContainer(
33+
MountableFile.forHostPath(Paths.get("src/test/resources/com/baeldung/java/io/jsch/nopassphrase/id_rsa.pub")),
34+
"/home/" + USERNAME + "/.ssh/keys/id_rsa.pub"
35+
)
36+
.withCopyFileToContainer(
37+
MountableFile.forHostPath(Paths.get("src/test/resources/com/baeldung/java/io/jsch/passphrase/id_rsa.pub")),
38+
"/home/" + USERNAME + "/.ssh/keys/id_rsa2.pub"
39+
);
40+
41+
@Test
42+
void whenTheHostKeyIsUnknown_thenTheConnectionFails() throws Exception {
43+
JSch jSch = new JSch();
44+
45+
Session session = jSch.getSession(USERNAME, sftpContainer.getHost(), sftpContainer.getMappedPort(SFTP_PORT));
46+
47+
session.setUserInfo(new BaseUserInfo() {});
48+
49+
assertThrows(JSchUnknownHostKeyException.class, () -> session.connect());
50+
}
51+
52+
@Test
53+
void whenNoAuthenticationIsProvided_thenTheConnectionFails() throws Exception {
54+
JSch jSch = new JSch();
55+
56+
Session session = jSch.getSession(USERNAME, sftpContainer.getHost(), sftpContainer.getMappedPort(SFTP_PORT));
57+
58+
session.setUserInfo(new BaseUserInfo() {
59+
60+
@Override
61+
public boolean promptYesNo(String message) {
62+
if (message.startsWith("The authenticity of host")) {
63+
return true;
64+
}
65+
66+
return false;
67+
}
68+
});
69+
70+
JSchException exception = assertThrows(JSchException.class, () -> session.connect());
71+
assertEquals("Auth cancel for methods 'publickey,password,keyboard-interactive'", exception.getMessage());
72+
}
73+
74+
@Test
75+
void whenPasswordAuthIsProvided_thenTheConnectionSucceeds() throws Exception {
76+
JSch jSch = new JSch();
77+
78+
Session session = jSch.getSession(USERNAME, sftpContainer.getHost(), sftpContainer.getMappedPort(SFTP_PORT));
79+
80+
session.setUserInfo(new BaseUserInfo() {
81+
@Override
82+
public String getPassword() {
83+
return PASSWORD;
84+
}
85+
86+
@Override
87+
public boolean promptPassword(String message) {
88+
return true;
89+
}
90+
91+
@Override
92+
public boolean promptYesNo(String message) {
93+
if (message.startsWith("The authenticity of host")) {
94+
return true;
95+
}
96+
97+
return false;
98+
}
99+
});
100+
101+
session.connect();
102+
}
103+
104+
@Test
105+
void givenAnOpenSSHKey_whenKeyAuthIsProvided_thenTheConnectionSucceeds() throws Exception {
106+
JSch jSch = new JSch();
107+
jSch.addIdentity("src/test/resources/com/baeldung/java/io/jsch/nopassphrase/id_rsa");
108+
109+
Session session = jSch.getSession(USERNAME, sftpContainer.getHost(), sftpContainer.getMappedPort(SFTP_PORT));
110+
111+
session.setUserInfo(new BaseUserInfo() {
112+
@Override
113+
public boolean promptYesNo(String message) {
114+
if (message.startsWith("The authenticity of host")) {
115+
return true;
116+
}
117+
118+
return false;
119+
}
120+
});
121+
122+
session.connect();
123+
}
124+
125+
@Test
126+
void givenAPuttyKey_whenKeyAuthIsProvided_thenTheConnectionSucceeds() throws Exception {
127+
JSch jSch = new JSch();
128+
jSch.addIdentity("src/test/resources/com/baeldung/java/io/jsch/nopassphrase/id_rsa.ppk");
129+
130+
Session session = jSch.getSession(USERNAME, sftpContainer.getHost(), sftpContainer.getMappedPort(SFTP_PORT));
131+
132+
session.setUserInfo(new BaseUserInfo() {
133+
@Override
134+
public boolean promptYesNo(String message) {
135+
if (message.startsWith("The authenticity of host")) {
136+
return true;
137+
}
138+
139+
return false;
140+
}
141+
});
142+
143+
session.connect();
144+
}
145+
146+
@Test
147+
void givenAnOpenSSHKeyWithPassphrase_whenKeyAuthIsProvided_thenTheConnectionSucceeds() throws Exception {
148+
JSch jSch = new JSch();
149+
jSch.addIdentity("src/test/resources/com/baeldung/java/io/jsch/passphrase/id_rsa");
150+
151+
Session session = jSch.getSession(USERNAME, sftpContainer.getHost(), sftpContainer.getMappedPort(SFTP_PORT));
152+
153+
session.setUserInfo(new BaseUserInfo() {
154+
@Override
155+
public String getPassphrase() {
156+
return "te5tPa55word";
157+
}
158+
159+
@Override
160+
public boolean promptPassphrase(String message) {
161+
return true;
162+
}
163+
164+
@Override
165+
public boolean promptYesNo(String message) {
166+
if (message.startsWith("The authenticity of host")) {
167+
return true;
168+
}
169+
170+
return false;
171+
}
172+
});
173+
174+
session.connect();
175+
}
176+
}

0 commit comments

Comments
 (0)