Repository: mina-sshd
Updated Branches:
  refs/heads/master 5fc90bd04 -> 598c991fe


[SSHD-821] Support for async keyboard authentication


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/5c1c8a98
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/5c1c8a98
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/5c1c8a98

Branch: refs/heads/master
Commit: 5c1c8a9830ad5b622b15055bfc7205aa2fd53e98
Parents: 5fc90bd
Author: Guillaume Nodet <gno...@apache.org>
Authored: Wed Apr 18 14:17:28 2018 +0200
Committer: Guillaume Nodet <gno...@apache.org>
Committed: Thu Apr 19 08:42:02 2018 +0200

----------------------------------------------------------------------
 .../sshd/server/auth/AsyncAuthException.java    |  94 ++++++++++++
 .../org/apache/sshd/server/auth/UserAuth.java   |   6 +-
 .../auth/password/PasswordAuthenticator.java    |   5 +-
 .../auth/pubkey/PublickeyAuthenticator.java     |   4 +-
 .../server/session/ServerUserAuthService.java   |  21 ++-
 .../server/auth/AsyncAuthInteractiveTest.java   | 106 +++++++++++++
 .../apache/sshd/server/auth/AsyncAuthTest.java  | 103 +++++++++++++
 .../sshd/server/auth/AsyncAuthTestBase.java     | 147 +++++++++++++++++++
 sshd-mina/pom.xml                               |   2 +
 9 files changed, 483 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-core/src/main/java/org/apache/sshd/server/auth/AsyncAuthException.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/auth/AsyncAuthException.java 
b/sshd-core/src/main/java/org/apache/sshd/server/auth/AsyncAuthException.java
new file mode 100644
index 0000000..0a95986
--- /dev/null
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/auth/AsyncAuthException.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sshd.server.auth;
+
+import java.lang.reflect.Array;
+import java.util.function.Consumer;
+
+import org.apache.sshd.common.RuntimeSshException;
+
+/**
+ *
+ *
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public class AsyncAuthException extends RuntimeSshException {
+
+    private static final long serialVersionUID = 6741236101797649869L;
+
+    protected Object listener;
+    protected Boolean authed;
+
+    public AsyncAuthException() {
+        super();
+    }
+
+    public void setAuthed(boolean authed) {
+        Object listener;
+        synchronized (this) {
+            if (this.authed != null) {
+                return;
+            }
+            this.authed = authed;
+            listener = this.listener;
+        }
+        if (listener != null) {
+            if (listener instanceof Consumer) {
+                asListener(listener).accept(authed);
+            } else {
+                int l = Array.getLength(listener);
+                for (int i = 0; i < l; i++) {
+                    Consumer<Boolean> lst = asListener(Array.get(listener, i));
+                    if (lst != null) {
+                        lst.accept(authed);
+                    }
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected static Consumer<Boolean> asListener(Object listener) {
+        return (Consumer<Boolean>) listener;
+    }
+
+    public void addListener(Consumer<Boolean> listener) {
+        Boolean result;
+        synchronized (this) {
+            if (this.listener == null) {
+                this.listener = listener;
+            } else if (this.listener instanceof Consumer) {
+                this.listener = new Object[] {this.listener, listener };
+            } else {
+                Object[] ol = (Object[]) this.listener;
+                int l = ol.length;
+                Object[] nl = new Object[l + 1];
+                System.arraycopy(ol, 0, nl, 0, l);
+                nl[l] = listener;
+                this.listener = nl;
+            }
+            result = this.authed;
+        }
+        if (result != null) {
+            listener.accept(result);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java 
b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java
index 91b919c..d26da38 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuth.java
@@ -42,9 +42,10 @@ public interface UserAuth extends ServerSessionHolder, 
UserAuthInstance<ServerSe
      * @param buffer   the request buffer containing parameters specific to 
this request
      * @return <code>true</code> if the authentication succeeded, 
<code>false</code> if the authentication
      * failed and {@code null} if not finished yet
+     * @throws AsyncAuthException if the service is willing to perform an 
asynchronous authentication
      * @throws Exception if the authentication fails
      */
-    Boolean auth(ServerSession session, String username, String service, 
Buffer buffer) throws Exception;
+    Boolean auth(ServerSession session, String username, String service, 
Buffer buffer) throws AsyncAuthException, Exception;
 
     /**
      * Handle another step in the authentication process.
@@ -52,9 +53,10 @@ public interface UserAuth extends ServerSessionHolder, 
UserAuthInstance<ServerSe
      * @param buffer the request buffer containing parameters specific to this 
request
      * @return <code>true</code> if the authentication succeeded, 
<code>false</code> if the authentication
      * failed and {@code null} if not finished yet
+     * @throws AsyncAuthException if the service is willing to perform an 
asynchronous authentication
      * @throws Exception if the authentication fails
      */
-    Boolean next(Buffer buffer) throws Exception;
+    Boolean next(Buffer buffer) throws AsyncAuthException, Exception;
 
     /**
      * Free any system resources used by the module.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
index 902c128..fcc91e1 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/auth/password/PasswordAuthenticator.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sshd.server.auth.password;
 
+import org.apache.sshd.server.auth.AsyncAuthException;
 import org.apache.sshd.server.session.ServerSession;
 
 /**
@@ -36,6 +37,8 @@ public interface PasswordAuthenticator {
      * @return {@code true} indicating if authentication succeeded
      * @throws PasswordChangeRequiredException If the password is expired or
      * not strong enough to suit the server's policy
+     * @throws AsyncAuthException If the authentication is performed 
asynchronously
      */
-    boolean authenticate(String username, String password, ServerSession 
session) throws PasswordChangeRequiredException;
+    boolean authenticate(String username, String password, ServerSession 
session)
+            throws PasswordChangeRequiredException, AsyncAuthException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
index 5146ea7..2d7a908 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/auth/pubkey/PublickeyAuthenticator.java
@@ -20,6 +20,7 @@ package org.apache.sshd.server.auth.pubkey;
 
 import java.security.PublicKey;
 
+import org.apache.sshd.server.auth.AsyncAuthException;
 import org.apache.sshd.server.session.ServerSession;
 
 /**
@@ -38,6 +39,7 @@ public interface PublickeyAuthenticator {
      * @param key      the key
      * @param session  the server session
      * @return a boolean indicating if authentication succeeded or not
+     * @throws AsyncAuthException If the authentication is performed 
asynchronously
      */
-    boolean authenticate(String username, PublicKey key, ServerSession 
session);
+    boolean authenticate(String username, PublicKey key, ServerSession 
session) throws AsyncAuthException;
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
index 28ea4d4..2c72b6d 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
@@ -55,6 +55,7 @@ import 
org.apache.sshd.common.util.closeable.AbstractCloseable;
 import org.apache.sshd.common.util.io.IoUtils;
 import org.apache.sshd.server.ServerAuthenticationManager;
 import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.auth.AsyncAuthException;
 import org.apache.sshd.server.auth.UserAuth;
 import org.apache.sshd.server.auth.UserAuthNoneFactory;
 import org.apache.sshd.server.auth.WelcomeBannerPhase;
@@ -143,7 +144,7 @@ public class ServerUserAuthService extends 
AbstractCloseable implements Service,
     }
 
     @Override
-    public void process(int cmd, Buffer buffer) throws Exception {
+    public synchronized void process(int cmd, Buffer buffer) throws Exception {
         Boolean authed = Boolean.FALSE;
         ServerSession session = getServerSession();
         boolean debugEnabled = log.isDebugEnabled();
@@ -196,6 +197,9 @@ public class ServerUserAuthService extends 
AbstractCloseable implements Service,
                 currentAuth = ValidateUtils.checkNotNull(factory.create(), "No 
authenticator created for method=%s", method);
                 try {
                     authed = currentAuth.auth(session, username, service, 
buffer);
+                } catch (AsyncAuthException async) {
+                    async.addListener(authenticated -> asyncAuth(cmd, buffer, 
authenticated));
+                    return;
                 } catch (Exception e) {
                     if (debugEnabled) {
                         log.debug("process({}) Failed ({}) to authenticate 
using factory method={}: {}",
@@ -228,6 +232,9 @@ public class ServerUserAuthService extends 
AbstractCloseable implements Service,
             buffer.rpos(buffer.rpos() - 1);
             try {
                 authed = currentAuth.next(buffer);
+            } catch (AsyncAuthException async) {
+                async.addListener(authenticated -> asyncAuth(cmd, buffer, 
authenticated));
+                return;
             } catch (Exception e) {
                 // Continue
                 if (debugEnabled) {
@@ -249,6 +256,18 @@ public class ServerUserAuthService extends 
AbstractCloseable implements Service,
         }
     }
 
+    protected synchronized void asyncAuth(int cmd, Buffer buffer, boolean 
authed) {
+        try {
+            if (authed) {
+                handleAuthenticationSuccess(cmd, buffer);
+            } else {
+                handleAuthenticationFailure(cmd, buffer);
+            }
+        } catch (Exception e) {
+            log.warn("Error performing async authentication: {}", 
e.getMessage(), e);
+        }
+    }
+
     protected void handleAuthenticationInProgress(int cmd, Buffer buffer) 
throws Exception {
         String username = (currentAuth == null) ? null : 
currentAuth.getUsername();
         if (log.isDebugEnabled()) {

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthInteractiveTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthInteractiveTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthInteractiveTest.java
new file mode 100644
index 0000000..42763ef
--- /dev/null
+++ 
b/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthInteractiveTest.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sshd.server.auth;
+
+import com.jcraft.jsch.ChannelShell;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.UserInfo;
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AsyncAuthInteractiveTest extends AsyncAuthTestBase {
+
+    public AsyncAuthInteractiveTest() {
+        super();
+    }
+
+    protected boolean authenticate() throws Exception {
+
+        JSch jsch = new JSch();
+        Session session;
+        ChannelShell channel;
+
+        session = jsch.getSession("whatever", "localhost", port);
+        session.setUserInfo(new UserInfo() {
+            @Override
+            public String getPassphrase() {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public String getPassword() {
+                return "whocares";
+            }
+
+            @Override
+            public boolean promptPassword(String s) {
+                return true;
+            }
+
+            @Override
+            public boolean promptPassphrase(String s) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public boolean promptYesNo(String s) {
+                return true;
+            }
+
+            @Override
+            public void showMessage(String s) {
+                // Do nothing
+            }
+        });
+        try {
+            session.connect();
+        } catch (JSchException e) {
+            switch (e.getMessage()) {
+                case "Auth cancel":
+                case "Auth fail":
+                    return false;
+                default:
+                    throw e;
+            }
+        }
+        channel = (ChannelShell) session.openChannel("shell");
+        channel.connect();
+
+        try {
+            channel.disconnect();
+        } catch (Exception ignore) {
+            // ignore
+        }
+
+        try {
+            session.disconnect();
+        } catch (Exception ignore) {
+            // ignore
+        }
+
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthTest.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthTest.java 
b/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthTest.java
new file mode 100644
index 0000000..5f8f591
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sshd.server.auth;
+
+import com.jcraft.jsch.ChannelShell;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.UserInfo;
+
+import org.junit.FixMethodOrder;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AsyncAuthTest extends AsyncAuthTestBase {
+
+    public AsyncAuthTest() {
+        super();
+    }
+
+    protected boolean authenticate() throws Exception {
+
+        JSch jsch = new JSch();
+        Session session;
+        ChannelShell channel;
+
+        session = jsch.getSession("whatever", "localhost", port);
+        session.setPassword("whocares");
+        session.setUserInfo(new UserInfo() {
+            @Override
+            public String getPassphrase() {
+                return null;
+            }
+
+            @Override
+            public String getPassword() {
+                return null;
+            }
+
+            @Override
+            public boolean promptPassword(String s) {
+                return false;
+            }
+
+            @Override
+            public boolean promptPassphrase(String s) {
+                return false;
+            }
+
+            @Override
+            public boolean promptYesNo(String s) {
+                return true;
+            } // Accept all server keys
+
+            @Override
+            public void showMessage(String s) {
+                // Do nothing
+            }
+        });
+        try {
+            session.connect();
+        } catch (JSchException e) {
+            if (e.getMessage().equals("Auth cancel")) {
+                return false;
+            } else {
+                throw e;
+            }
+        }
+        channel = (ChannelShell) session.openChannel("shell");
+        channel.connect();
+
+        try {
+            channel.disconnect();
+        } catch (Exception ignore) {
+        }
+
+        try {
+            session.disconnect();
+        } catch (Exception ignore) {
+        }
+
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthTestBase.java
----------------------------------------------------------------------
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthTestBase.java 
b/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthTestBase.java
new file mode 100644
index 0000000..4328117
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/AsyncAuthTestBase.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * http://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.apache.sshd.server.auth;
+
+import java.io.File;
+
+import com.jcraft.jsch.JSchException;
+
+import org.apache.sshd.common.FactoryManager;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.auth.password.PasswordAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.apache.sshd.util.test.EchoShellFactory;
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+public abstract class AsyncAuthTestBase extends BaseTestSupport {
+
+    SshServer server;
+    int port;
+
+    private PasswordAuthenticator authenticator;
+
+    public AsyncAuthTestBase() {
+        super();
+    }
+
+    public void startServer() throws Exception {
+        startServer(null);
+    }
+
+    public void startServer(Integer timeout) throws Exception {
+        if (server != null) {
+            fail("Server already started");
+        }
+        server = SshServer.setUpDefaultServer();
+        if (timeout != null) {
+            server.getProperties().put(FactoryManager.AUTH_TIMEOUT, 
timeout.toString());
+        }
+        server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new 
File("hostkey.ser").toPath()));
+        server.setPasswordAuthenticator((username, password, session) -> 
authenticator.authenticate(username, password, session));
+        server.setShellFactory(new EchoShellFactory());
+        server.start();
+        port = server.getPort();
+    }
+
+    @After
+    public void stopServer() throws Exception {
+        if (server != null) {
+            server.stop();
+        }
+        server = null;
+    }
+
+    @Test
+    public void testSyncAuthFailed() throws Exception {
+        startServer();
+        authenticator = (username, x, sess) -> false;
+        assertFalse(authenticate());
+    }
+
+    @Test
+    public void testSyncAuthSucceeded() throws Exception {
+        startServer();
+        authenticator = (username, x, sess) -> true;
+        assertTrue(authenticate());
+    }
+
+    @Test
+    public void testAsyncAuthFailed() throws Exception {
+        startServer();
+        authenticator = (username, x, sess) -> async(200, false);
+        assertFalse(authenticate());
+    }
+
+    @Test
+    public void testAsyncAuthSucceeded() throws Exception {
+        startServer();
+        authenticator = (username, x, sess) -> async(200, true);
+        assertTrue(authenticate());
+    }
+
+    @Test
+    public void testAsyncAuthTimeout() throws Exception {
+        startServer(500);
+        authenticator = (username, x, sess) -> asyncTimeout();
+        try {
+            authenticate();
+        } catch (JSchException e) {
+            assertTrue("Unexpected failure " + e.getMessage(), 
e.getMessage().startsWith("SSH_MSG_DISCONNECT"));
+        }
+    }
+
+    @Test
+    public void testAsyncAuthSucceededAfterTimeout() throws Exception {
+        startServer(500);
+        authenticator = (username, x, sess) -> async(1000, true);
+        try {
+            authenticate();
+        } catch (JSchException e) {
+            assertTrue("Unexpected failure " + e.getMessage(), 
e.getMessage().startsWith("SSH_MSG_DISCONNECT"));
+        }
+    }
+
+    private boolean asyncTimeout() {
+        throw new AsyncAuthException();
+    }
+
+    private boolean async(int delay, boolean result) {
+        AsyncAuthException auth = new AsyncAuthException();
+        new Thread(() -> doAsync(delay, result, auth)).start();
+        throw auth;
+    }
+
+    private void doAsync(int delay, boolean result, AsyncAuthException auth) {
+        try {
+            Thread.sleep(delay);
+        } catch (InterruptedException ignore) {
+            // ignore
+        } finally {
+            auth.setAuthed(result);
+        }
+    }
+
+    protected abstract boolean authenticate() throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/5c1c8a98/sshd-mina/pom.xml
----------------------------------------------------------------------
diff --git a/sshd-mina/pom.xml b/sshd-mina/pom.xml
index 1000f8a..bb62820 100644
--- a/sshd-mina/pom.xml
+++ b/sshd-mina/pom.xml
@@ -180,6 +180,8 @@
                         <exclude>**/MacTest.java</exclude>
                         <exclude>**/SpringConfigTest.java</exclude>
                         <exclude>**/ConcurrentConnectionTest.java</exclude>
+                        <exclude>**/AsyncAuthTest.java</exclude>
+                        <exclude>**/AsyncAuthInteractiveTest.java</exclude>
                     </excludes>
                         <!-- No need to re-run core tests that do not involve 
session creation -->
                     
<excludedGroups>org.apache.sshd.util.test.NoIoTestCase</excludedGroups>

Reply via email to