Skip to content

Commit 3edb85a

Browse files
committed
Add support for credentials
It is now possible to test authentication handling. You can restrict the server to accept only specific pairs of username and password. public class TestClass { @rule public final FakeSftpServerRule sftpServer = new FakeSftpServerRule() .addUser("username", "password"); ... } It is also possible to do this during the test using the same method. Fixes #6.
1 parent cd1cfaf commit 3edb85a

File tree

4 files changed

+219
-19
lines changed

4 files changed

+219
-19
lines changed

README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Fake SFTP Server Rule is published under the
1212
if you want to use it with an older version of Java.
1313

1414
I want to thank my former team SAM at ThoughtWorks for using this library and
15-
@OArtyomov for his feature request.
15+
@crizzis and @OArtyomov for their feature requests.
1616

1717

1818
## Installation
@@ -23,7 +23,7 @@ Fake SFTP Server Rule is available from
2323
<dependency>
2424
<groupId>com.github.stefanbirkner</groupId>
2525
<artifactId>fake-sftp-server-rule</artifactId>
26-
<version>1.3.1</version>
26+
<version>1.4.0</version>
2727
</dependency>
2828

2929

@@ -42,9 +42,19 @@ The Fake SFTP Server Rule is used by adding it to your test class.
4242

4343
This rule starts a server before your test and stops it afterwards.
4444

45-
You can interact with the SFTP server by using the SFTP protocol with an
46-
arbitrary username and password. (The server accepts every combination of
47-
username and password.)
45+
You can interact with the SFTP server by using the SFTP protocol with password
46+
authentication. By default the server accepts every pair of username and
47+
password, but you can restrict it to specific pairs.
48+
49+
public class TestClass {
50+
@Rule
51+
public final FakeSftpServerRule sftpServer = new FakeSftpServerRule()
52+
.addUser("username", "password");
53+
54+
...
55+
}
56+
57+
It is also possible to do this during the test using the same method.
4858

4959
The port of the server is obtained by `sftpServer.getPort()`. You can change it
5060
by calling `setPort(int)`. If you do this from within a test then the server

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</parent>
1010

1111
<artifactId>fake-sftp-server-rule</artifactId>
12-
<version>1.4.0-SNAPSHOT</version>
12+
<version>1.4.0</version>
1313
<packaging>jar</packaging>
1414

1515
<name>Fake SFTP Server Rule</name>

src/main/java/com/github/stefanbirkner/fakesftpserver/rule/FakeSftpServerRule.java

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.github.stefanbirkner.fakesftpserver.rule;
22

33
import org.apache.sshd.server.SshServer;
4-
import org.apache.sshd.server.auth.password.StaticPasswordAuthenticator;
54
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
5+
import org.apache.sshd.server.session.ServerSession;
66
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
77
import org.junit.rules.TestRule;
88
import org.junit.runner.Description;
@@ -15,6 +15,9 @@
1515
import java.nio.file.attribute.BasicFileAttributes;
1616
import java.nio.file.attribute.UserPrincipalLookupService;
1717
import java.nio.file.spi.FileSystemProvider;
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
import java.util.Objects;
1821
import java.util.Set;
1922

2023
import static com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder.newLinux;
@@ -35,9 +38,19 @@
3538
* }
3639
* </pre>
3740
* <p>This rule starts a server before your test and stops it afterwards.
38-
* <p>You can interact with the SFTP server by using the SFTP protocol with an
39-
* arbitrary username and password. (The server accepts every combination of
40-
* username and password.)
41+
* <p>You can interact with the SFTP server by using the SFTP protocol with
42+
* password authentication. By default the server accepts every pair of
43+
* username and password, buy you can restrict it to specific pairs.
44+
* <pre>
45+
* public class TestClass {
46+
* &#064;Rule
47+
* public final FakeSftpServerRule sftpServer = new FakeSftpServerRule()
48+
* .{@link #addUser(String, String) addUser}("username", "password");
49+
*
50+
* ...
51+
* }
52+
* </pre>
53+
* <p>It is also possible to do this during the test using the same method.
4154
* <p>The port of the server is obtained by
4255
* {@link #getPort() sftpServer.getPort()}. You can change it by calling
4356
* {@link #setPort(int)}. If you do this from within a test then the server gets
@@ -164,6 +177,7 @@ public FileVisitResult postVisitDirectory(
164177
return super.postVisitDirectory(dir, exc);
165178
}
166179
};
180+
private final Map<String, String> usernamesAndPasswords = new HashMap<>();
167181
private int port = 23454;
168182

169183
private FileSystem fileSystem;
@@ -201,6 +215,24 @@ public FakeSftpServerRule setPort(
201215
return this;
202216
}
203217

218+
/**
219+
* Register a username with its password. After registering a username
220+
* it is only possible to connect to the server with one of the registered
221+
* username/password pairs.
222+
* <p>If {@code addUser} is called multiple times with the same username but
223+
* different passwords then the last password is effective.
224+
* @param username the username.
225+
* @param password the password for the specified username.
226+
* @return the rule itself.
227+
*/
228+
public FakeSftpServerRule addUser(
229+
String username,
230+
String password
231+
) {
232+
usernamesAndPasswords.put(username, password);
233+
return this;
234+
}
235+
204236
private void restartServer() {
205237
try {
206238
server.stop();
@@ -384,7 +416,7 @@ private SshServer startServer(
384416
SshServer server = SshServer.setUpDefaultServer();
385417
server.setPort(port);
386418
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
387-
server.setPasswordAuthenticator(new StaticPasswordAuthenticator(true));
419+
server.setPasswordAuthenticator(this::authenticate);
388420
server.setSubsystemFactories(singletonList(new SftpSubsystemFactory()));
389421
/* When a channel is closed SshServer calls close() on the file system.
390422
* In order to use the file system for multiple channels/sessions we
@@ -396,6 +428,18 @@ private SshServer startServer(
396428
return server;
397429
}
398430

431+
private boolean authenticate(
432+
String username,
433+
String password,
434+
ServerSession session
435+
) {
436+
return usernamesAndPasswords.isEmpty()
437+
|| Objects.equals(
438+
usernamesAndPasswords.get(username),
439+
password
440+
);
441+
}
442+
399443
private void ensureDirectoryOfPathExists(
400444
Path path
401445
) throws IOException {

src/test/java/com/github/stefanbirkner/fakesftpserver/rule/FakeSftpServerRuleTest.java

Lines changed: 154 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import com.jcraft.jsch.*;
55
import org.apache.commons.io.IOUtils;
6+
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
67
import org.junit.Test;
78
import org.junit.experimental.runners.Enclosed;
89
import org.junit.runner.RunWith;
@@ -62,14 +63,6 @@ public void a_file_that_is_written_to_the_SFTP_server_can_be_read() {
6263
}
6364

6465
public static class connection {
65-
@Test
66-
public void the_server_accepts_connections_with_password() {
67-
FakeSftpServerRule sftpServer = new FakeSftpServerRule();
68-
executeTestWithRule(
69-
() -> connectToServer(sftpServer),
70-
sftpServer
71-
);
72-
}
7366

7467
@Test
7568
public void multiple_connections_to_the_server_are_possible() {
@@ -94,6 +87,159 @@ public void a_client_can_connect_to_the_server_at_a_user_specified_port() {
9487
}
9588
}
9689

90+
@RunWith(Enclosed.class)
91+
public static class authentication {
92+
public static class server_without_credentials {
93+
@Test
94+
public void the_server_accepts_connections_with_password() {
95+
FakeSftpServerRule sftpServer = new FakeSftpServerRule();
96+
executeTestWithRule(
97+
() -> {
98+
Session session = createSessionWithCredentials(
99+
sftpServer,
100+
"dummy user",
101+
"dummy password"
102+
);
103+
session.connect(TIMEOUT);
104+
},
105+
sftpServer
106+
);
107+
}
108+
}
109+
110+
public static class server_with_credentials_immediately_set {
111+
@Test
112+
public void the_server_accepts_connections_with_correct_password() {
113+
FakeSftpServerRule sftpServer = new FakeSftpServerRule()
114+
.addUser("dummy user", "dummy password");
115+
executeTestWithRule(
116+
() -> {
117+
Session session = createSessionWithCredentials(
118+
sftpServer,
119+
"dummy user",
120+
"dummy password"
121+
);
122+
session.connect(TIMEOUT);
123+
},
124+
sftpServer
125+
);
126+
}
127+
128+
129+
@Test
130+
public void the_server_rejects_connections_with_wrong_password() {
131+
FakeSftpServerRule sftpServer = new FakeSftpServerRule()
132+
.addUser("dummy user", "correct password");
133+
executeTestWithRule(
134+
() -> {
135+
Session session = createSessionWithCredentials(
136+
sftpServer,
137+
"dummy user",
138+
"wrong password"
139+
);
140+
assertAuthenticationFails(
141+
() -> session.connect(TIMEOUT)
142+
);
143+
},
144+
sftpServer
145+
);
146+
}
147+
148+
@Test
149+
public void the_last_password_is_effective_if_addUser_is_called_multiple_times() {
150+
FakeSftpServerRule sftpServer = new FakeSftpServerRule()
151+
.addUser("dummy user", "first password")
152+
.addUser("dummy user", "second password");
153+
executeTestWithRule(
154+
() -> {
155+
Session session = createSessionWithCredentials(
156+
sftpServer,
157+
"dummy user",
158+
"second password"
159+
);
160+
session.connect(TIMEOUT);
161+
},
162+
sftpServer
163+
);
164+
}
165+
}
166+
167+
public static class server_with_credentials_set_during_test {
168+
@Test
169+
public void the_server_accepts_connections_with_correct_password() {
170+
FakeSftpServerRule sftpServer = new FakeSftpServerRule();
171+
executeTestWithRule(
172+
() -> {
173+
sftpServer.addUser("dummy user", "dummy password");
174+
Session session = createSessionWithCredentials(
175+
sftpServer,
176+
"dummy user",
177+
"dummy password"
178+
);
179+
session.connect(TIMEOUT);
180+
},
181+
sftpServer
182+
);
183+
}
184+
185+
@Test
186+
public void the_server_rejects_connections_with_wrong_password() {
187+
FakeSftpServerRule sftpServer = new FakeSftpServerRule();
188+
executeTestWithRule(
189+
() -> {
190+
sftpServer.addUser("dummy user", "correct password");
191+
Session session = createSessionWithCredentials(
192+
sftpServer,
193+
"dummy user",
194+
"wrong password"
195+
);
196+
assertAuthenticationFails(
197+
() -> session.connect(TIMEOUT)
198+
);
199+
},
200+
sftpServer
201+
);
202+
}
203+
204+
@Test
205+
public void the_last_password_is_effective_if_addUser_is_called_multiple_times() {
206+
FakeSftpServerRule sftpServer = new FakeSftpServerRule();
207+
executeTestWithRule(
208+
() -> {
209+
sftpServer
210+
.addUser("dummy user", "first password")
211+
.addUser("dummy user", "second password");
212+
Session session = createSessionWithCredentials(
213+
sftpServer,
214+
"dummy user",
215+
"second password"
216+
);
217+
session.connect(TIMEOUT);
218+
},
219+
sftpServer
220+
);
221+
}
222+
}
223+
224+
private static Session createSessionWithCredentials(
225+
FakeSftpServerRule sftpServer,
226+
String username,
227+
String password
228+
) throws JSchException {
229+
return FakeSftpServerRuleTest.createSessionWithCredentials(
230+
username, password, sftpServer.getPort()
231+
);
232+
}
233+
234+
private static void assertAuthenticationFails(
235+
ThrowingCallable connectToServer
236+
) {
237+
assertThatThrownBy(connectToServer)
238+
.isInstanceOf(JSchException.class)
239+
.hasMessage("Auth fail");
240+
}
241+
}
242+
97243
@RunWith(Enclosed.class)
98244
public static class file_upload {
99245
public static class a_text_file {

0 commit comments

Comments
 (0)