I guess attachments are not allowed on the mailing list, so I've pasted the
code inline:
-------------------------------
SftpServer.java == BEGIN
-------------------------------
package com.example;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.sftp.SftpSubsystem;
import org.apache.sshd.util.EchoShellFactory;
public class SftpServer implements PasswordAuthenticator {
private final ConcurrentMap<String, String> authMap = new
ConcurrentHashMap<String, String>();
private final AtomicBoolean started = new AtomicBoolean(false);
private SshServer sshd;
@SuppressWarnings("unchecked")
public SftpServer(int port, File privateKey) throws IOException {
sshd = SshServer.setUpDefaultServer();
sshd.setPort(port);
sshd.setKeyPairProvider(new FileKeyPairProvider(new
String[]{privateKey.getAbsolutePath()}));
sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new
SftpSubsystem.Factory()));
sshd.setShellFactory(new EchoShellFactory());
// this class manages authentication
sshd.setPasswordAuthenticator(this);
sshd.start();
}
public void createSession(String username, String password) {
authMap.put(username, password);
System.err.println(String.format("CREATE-SESSION: SftpServer
identity:%s, authMap identity:%s, authMap:%s, user:%s, pass:%s",
System.identityHashCode(this),
System.identityHashCode(authMap), authMap, username, password));
}
public boolean authenticate(String username, String password,
ServerSession session) {
System.err.println(String.format("AUTHENTICATE: SftpServer
identity:%s, authMap identity:%s, authMap:%s, user:%s, pass:%s",
System.identityHashCode(this),
System.identityHashCode(authMap), authMap, username, password));
if(authMap.containsKey(username)) {
return authMap.get(username).equals(password);
}
return false;
}
public void start() throws Exception {
if(!started.compareAndSet(false, true)) {
sshd.start();
started.set(true);
}
}
public void stop() throws Exception {
if(started.get()) {
sshd.stop();
}
}
}
-------------------------------
SftpServer.java == END
-------------------------------
-------------------------------
SftpServerTest.java == BEGIN
-------------------------------
package com.example;
import static org.junit.Assert.fail;
import java.io.File;
import java.net.ServerSocket;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.UserInfo;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Logger;
import com.jcraft.jsch.Session;
public class SftpServerTest {
private SftpServer server;
private static int port;
private static File privateKey = new
File("src/test/resources/hostkey.pem");
private Session session;
private String username = "foo";
private String password = "bar";
private String hostname = "localhost";
@BeforeClass
public static void beforeClass() {
try {
ServerSocket s = new ServerSocket(0);
port = s.getLocalPort();
s.close();
System.err.println("port chosen is "+port);
} catch(Exception e) {
fail("Test setup fails trying to reserve an unused port
"+e.getMessage());
}
if(privateKey.exists() == false) {
fail("Test setup fails because private RSA key file does
not exist "+privateKey.getAbsolutePath());
}
}
@Before
public void setUp() throws Exception {
server = new SftpServer(port, privateKey );
}
@After
public void tearDown() throws Exception {
if(session != null) { session.disconnect(); }
server.stop();
}
@Test
public void testCreateSingleSession() throws Exception {
server.createSession(username, password);
session = getSession(username, password, hostname);
}
@Test
public void testCreateMultipleSessions() throws Exception {
server.createSession(username, password);
session = getSession(username, password, hostname);
String username2 = username+2;
String password2 = password+2;
server.createSession(username2, password2);
session = getSession(username2, password2, hostname);
String username3 = username+3;
String password3 = password+3;
server.createSession(username3, password3);
session = getSession(username3, password3, hostname);
}
/**
* Get a client session that we can use to SFTP upload files with
* @return a client session that we can use to SFTP files
* @throws JSchException
*/
private Session getSession(String username, final String password,
String hostname) throws JSchException {
JSch sch = new JSch();
JSch.setLogger(new Logger(){
public boolean isEnabled(int arg0) {
return true;
}
public void log(int i, String s) {
System.out.println("Log(jsch "+i+"):" + s);
}
});
Session session = sch.getSession(username, hostname, port);
session.setUserInfo(new UserInfo() {
public String getPassphrase() {
System.err.println("getPassphrase called");
return null;
}
public String getPassword() {
return password;
}
public boolean promptPassphrase(String s) {
System.err.println("promptPassphrase: "+s);
return false;
}
public boolean promptPassword(String s) {
System.err.println("promptPassword: "+s);
return true;
}
public boolean promptYesNo(String s) {
System.err.println("promptYesNo: "+s);
return true;
}
public void showMessage(String s) {
System.err.println("showMessage: "+s);
}
});
session.connect();
return session;
}
}
-------------------------------
SftpServerTest.java == END
-------------------------------
From: Davis Ford [mailto:[email protected]]
Sent: Wednesday, September 14, 2011 12:29 PM
To: [email protected]
Subject: SFTP/SSHD passwordauthenticator test case
Here's a follow up to my last email with a demo class / test case that
illustrates the failure.
Add this code to the apache-sshd/sshd-core project under
src/test/java/com/example
When you run the test the first case passes. It spins up a new Sshd/SFTP
server and connects to it with the JSch client, and authenticates just fine
with the user/pass obtained via the ConcurrentHashMap.
The second test case fails. It tries to connect multiple sessions with
different user/pass combos. You'll see the following printed on
system.err.println =>
First, we create a new session for foo=bar:
CREATE-SESSION: SftpServer identity:1190000432, authMap identity:822056113,
authMap:{foo=bar}, user:foo, pass:bar
Then we authenticate -- works ok, you'll see foo=bar is in the map
AUTHENTICATE: SftpServer identity:1526115339, authMap identity:2023306452,
authMap:{foo=bar}, user:foo, pass:bar
Now, we create a new session for foo2=bar2, note that it prints the contents of
the map after this call and it contains foo2=bar2
CREATE-SESSION: SftpServer identity:1190000432, authMap identity:822056113,
authMap:{foo2=bar2, foo=bar}, user:foo2, pass:bar2
Then we try to authenticate, but it fails. foo2=bar2 is not in the map, and
the identity hashcode is different.
AUTHENTICATE: SftpServer identity:1526115339, authMap identity:2023306452,
authMap:{foo=bar}, user:foo2, pass:bar2
Note that the identity hashcodes change between calls to createSession( ) in
the test, and authenticate( ) coming in from apache-sshd. If you look at the
code, you'll see that I don't manipulate the ConcurrentMap anywhere in the code
or the test case other than adding an entry to it, and it is a private final
instance variable.
So, why doesn't this work? Something in the mina code is using a stale
instance of the SftpServer.java class it seems?
Any ideas?
Regards,
Davis