/*
 * 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 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;

import com.jcraft.jsch.JSchException;

/**
 * @author <a href="mailto:dev@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;

}

@@ -849,15 +849,71 @@
 ava.
+n
 io.
-File
+file.Files;%0Aimport java.nio.file.Path;%0Aimport java.util.Map
 ;%0A%0Ai
@@ -1937,32 +1937,60 @@
 l) %7B%0A           
+ Map%3CString, Object%3E props =
  server.getPrope
@@ -1996,16 +1996,35 @@
 erties()
+;%0A            props
 .put(Fac
@@ -2071,32 +2071,157 @@
 ng());%0A        %7D
+%0A%0A        Path tmpDir = Files.createDirectories(getTempTargetFolder());%0A        Path keyFile = tmpDir.resolve(%22hostkey.ser%22);
 %0A        server.
@@ -2278,40 +2278,15 @@
 der(
-new File(%22hostkey.ser%22).toPath()
+keyFile
 ));%0A

Create temporary serialized host key file for tests under 'target/temp' folder