/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.component.sshremoteaccess.internal;
import static org.easymock.EasyMock.notNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.QuoteMode;
import org.apache.commons.io.IOUtils;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.CommandFactory;
import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.UserAuth;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.auth.password.UserAuthPasswordFactory;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils;
import de.rcenvironment.core.communication.sshconnection.SshConnectionService;
import de.rcenvironment.core.communication.testutils.PlatformServiceDefaultStub;
import de.rcenvironment.core.component.model.api.ComponentInstallation;
import de.rcenvironment.core.component.registration.api.ComponentRegistry;
import de.rcenvironment.core.component.sshremoteaccess.SshRemoteAccessConstants;
import de.rcenvironment.core.utils.ssh.jsch.JschSessionFactory;
import de.rcenvironment.core.utils.ssh.jsch.SshParameterException;
/**
* Tests for SSH Remote Access Service.
*
* @author Brigitte Boden
* @author Robert Mischke (8.0.0 id adaptations)
*/
public class SshRemoteAccessClientServiceImplTest {
private SshServer sshServer;
private SshConnectionService connectionService;
private SshRemoteAccessClientServiceImpl remoteAccessService;
private final InstanceNodeSessionId dummyNodeId = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("dummy");
private ComponentRegistry mockRegistry;
/**
* Set up a dummy ssh server to connect to and setup mock connection service.
*
* @throws IOException on unexpected error
* @throws SshParameterException on SSH error
* @throws JSchException on SSH error
**/
@SuppressWarnings("serial")
@Before
public void setup() throws IOException, JSchException, SshParameterException {
sshServer = SshServer.setUpDefaultServer();
sshServer.setPort(SshRemoteAccessClientTestConstants.PORT);
sshServer.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
sshServer.setUserAuthFactories(new ArrayList<NamedFactory<UserAuth>>() {
{
add(new UserAuthPasswordFactory());
}
});
sshServer.setPasswordAuthenticator(new PasswordAuthenticator() {
@Override
public boolean authenticate(String username, String password, ServerSession session) {
return (username.equals(SshRemoteAccessClientTestConstants.USER) && password
.equals(SshRemoteAccessClientTestConstants.PASSWORD));
}
});
// Command factory that returns the correct version for the command "ra protocol-version"
sshServer.setCommandFactory(new CommandFactory() {
@Override
public Command createCommand(String commandString) {
if (commandString.equals("ra list-tools") || commandString.equals("ra list-wfs")) {
final String stdout;
if (commandString.equals("ra list-tools")) {
CSVFormat csvFormat = CSVFormat.newFormat(' ').withQuote('"').withQuoteMode(QuoteMode.ALL);
stdout = csvFormat.format(SshRemoteAccessClientTestConstants.TOOL_NAME,
SshRemoteAccessClientTestConstants.TOOL_VERSION, SshRemoteAccessClientTestConstants.HOST_ID,
SshRemoteAccessClientTestConstants.HOST_NAME);
} else {
stdout =
"1\n4\n" + SshRemoteAccessClientTestConstants.WF_NAME + "\n" + SshRemoteAccessClientTestConstants.WF_VERSION
+ "\n" + SshRemoteAccessClientTestConstants.HOST_NAME + "\n" + SshRemoteAccessClientTestConstants.HOST_ID;
}
return new Command() {
/** Test constant. */
public static final String EMPTY_STRING = "";
protected ExitCallback exitCallback;
private String stderr;
private int exitValue;
private OutputStream stdoutStream;
private OutputStream stderrStream;
@Override
public void setInputStream(InputStream in) {}
@Override
public void setOutputStream(OutputStream out) {
this.stdoutStream = out;
}
@Override
public void setErrorStream(OutputStream err) {
this.stderrStream = err;
}
@Override
public void setExitCallback(ExitCallback callback) {
this.exitCallback = callback;
}
@Override
public void start(Environment env) throws IOException {
if (stdout != null) {
stdoutStream.write(stdout.getBytes());
} else {
stdoutStream.write(EMPTY_STRING.getBytes());
}
if (stderr != null) {
stderrStream.write(stderr.getBytes());
} else {
stderrStream.write(EMPTY_STRING.getBytes());
}
stdoutStream.flush();
stderrStream.flush();
IOUtils.closeQuietly(stdoutStream);
IOUtils.closeQuietly(stderrStream);
exitCallback.onExit(exitValue);
}
@Override
public void destroy() {}
};
} else {
throw new IllegalArgumentException("Unknown command: " + commandString);
}
}
});
sshServer.start();
Session session =
JschSessionFactory.setupSession(SshRemoteAccessClientTestConstants.LOCALHOST, SshRemoteAccessClientTestConstants.PORT,
SshRemoteAccessClientTestConstants.USER, null, SshRemoteAccessClientTestConstants.PASSWORD, null);
connectionService = new MockSshConnectionService(session);
remoteAccessService = new SshRemoteAccessClientServiceImpl();
remoteAccessService.bindSSHConnectionService(connectionService);
remoteAccessService.bindPlatformService(new PlatformServiceDefaultStub() {
@Override
public InstanceNodeSessionId getLocalInstanceNodeSessionId() {
return dummyNodeId;
}
});
mockRegistry = EasyMock.createNiceMock(ComponentRegistry.class);
remoteAccessService.bindComponentRegistry(mockRegistry);
}
/**
* Test updating ssh remote access components.
*
*/
@Test(timeout = SshRemoteAccessClientTestConstants.TIMEOUT)
public void testUpdatingRemoteAccessComponents() {
EasyMock.reset(mockRegistry);
mockRegistry.addComponent(notNull(ComponentInstallation.class));
EasyMock.expectLastCall().andAnswer(new IAnswer<Void>() {
@Override
public Void answer() throws Throwable {
ComponentInstallation ci = (ComponentInstallation) EasyMock.getCurrentArguments()[0];
assertNotNull(ci);
assertEquals(SshRemoteAccessClientTestConstants.TOOL_VERSION, ci.getComponentRevision().getComponentInterface()
.getVersion());
assertEquals(SshRemoteAccessConstants.GROUP_NAME_TOOLS, ci.getComponentRevision().getComponentInterface()
.getGroupName());
return null;
}
});
EasyMock.expectLastCall().andAnswer(new IAnswer<Void>() {
@Override
public Void answer() throws Throwable {
ComponentInstallation ci = (ComponentInstallation) EasyMock.getCurrentArguments()[0];
assertNotNull(ci);
assertEquals(SshRemoteAccessClientTestConstants.WF_VERSION, ci.getComponentRevision().getComponentInterface()
.getVersion());
assertEquals(SshRemoteAccessConstants.GROUP_NAME_WFS, ci.getComponentRevision().getComponentInterface()
.getGroupName());
return null;
}
});
EasyMock.replay(mockRegistry);
remoteAccessService.updateSshRemoteAccessComponents();
}
/**
* Stop ssh server.
*
* @throws InterruptedException on error when stopping the server
* @throws IOException on unexpected error
**/
@After
public void tearDown() throws InterruptedException, IOException {
sshServer.stop();
}
}