diff options
Diffstat (limited to 'appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd')
8 files changed, 718 insertions, 0 deletions
diff --git a/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/.settings/org.eclipse.wst.common.project.facet.core.xml b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/.settings/org.eclipse.wst.common.project.facet.core.xml new file mode 100644 index 000000000..f4ef8aa0a --- /dev/null +++ b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/.settings/org.eclipse.wst.common.project.facet.core.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<faceted-project> + <installed facet="java" version="1.8"/> +</faceted-project> diff --git a/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/pom.xml b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/pom.xml new file mode 100644 index 000000000..54b7ea428 --- /dev/null +++ b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/pom.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.openecomp.appc</groupId> + <artifactId>appc-ssh-adapter</artifactId> + <version>1.0.0</version> + </parent> + + <artifactId>appc-ssh-adapter-sshd</artifactId> + <packaging>bundle</packaging> + + <dependencies> + <dependency> + <groupId>org.openecomp.appc</groupId> + <artifactId>appc-ssh-adapter-api</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-core</artifactId> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.att.eelf</groupId> + <artifactId>eelf-core</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + <dependency> + <groupId>org.openecomp.appc</groupId> + <artifactId>appc-common</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.openecomp.sdnc.core</groupId> + <artifactId>dblib-provider</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Export-Service>org.openecomp.appc.adapter.ssh.SshAdapter</Export-Service> + <Private-Package>org.openecomp.appc.adapter.ssh.impl.*</Private-Package> + <Import-Package>!org.apache.log,!org.apache.commons.logging,!groovy.lang,!javax.jms,!org.codehaus.commons.compiler,!org.codehaus.groovy.*,!org.codehaus.janino,!com.ibm.icu.*,!com.sun.faces.*,!org.jasypt.*,*</Import-Package> + <Embed-Dependency>!dblib-provider,appc-common,jasypt,eelf-core,logback-core,logback-classic;scope=compile|runtime;inline=false</Embed-Dependency> + <Embed-Transitive>true</Embed-Transitive> + </instructions> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterSshd.java b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterSshd.java new file mode 100644 index 000000000..44a6fb4ce --- /dev/null +++ b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterSshd.java @@ -0,0 +1,33 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.adapter.ssh.sshd; + +import org.openecomp.appc.adapter.ssh.SshAdapter; +import org.openecomp.appc.adapter.ssh.SshConnection; + +public class SshAdapterSshd implements SshAdapter { + + @Override + public SshConnection getConnection(String host, int port, String username, String password) { + return new SshConnectionSshd(host, port, username, password); + } +} diff --git a/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshConnectionSshd.java b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshConnectionSshd.java new file mode 100644 index 000000000..4d8b83b6f --- /dev/null +++ b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshConnectionSshd.java @@ -0,0 +1,172 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.adapter.ssh.sshd; + +import org.apache.sshd.ClientChannel; +import org.apache.sshd.ClientSession; +import org.apache.sshd.SshClient; +import org.apache.sshd.client.channel.ChannelExec; +import org.apache.sshd.client.future.AuthFuture; +import org.apache.sshd.client.future.OpenFuture; +import org.apache.sshd.common.KeyPairProvider; +import org.apache.sshd.common.keyprovider.FileKeyPairProvider; +import org.openecomp.appc.adapter.ssh.SshConnection; +import org.openecomp.appc.adapter.ssh.SshException; +import org.openecomp.appc.encryption.EncryptionTool; +import com.att.eelf.configuration.EELFLogger; +import com.att.eelf.configuration.EELFManager; + +import java.io.OutputStream; +import java.security.KeyPair; + +/** + * Implementation of SshConnection interface based on Apache MINA SSHD library. + */ +class SshConnectionSshd implements SshConnection { + + private static final EELFLogger logger = EELFManager.getInstance().getApplicationLogger(); + + private static final long AUTH_TIMEOUT = 60000; + private static final long EXEC_TIMEOUT = 120000; + + private String host; + private int port; + private String username; + private String password; + private long timeout = EXEC_TIMEOUT; + private String keyFile; + private SshClient sshClient; + private ClientSession clientSession; + + public SshConnectionSshd(String host, int port, String username, String password, String keyFile) { + this.host = host; + this.port = port; + this.username = username; + this.password = password; + this.keyFile = keyFile; + } + + public SshConnectionSshd(String host, int port, String username, String password) { + this(host, port, username, password, null); + } + + public SshConnectionSshd(String host, int port, String keyFile) { + this(host, port, null, null, keyFile); + } + + @Override + public void connect() { + sshClient = SshClient.setUpDefaultClient(); + sshClient.start(); + try { + clientSession = sshClient.connect(EncryptionTool.getInstance().decrypt(username), host, port).await().getSession(); + if(password != null) { + clientSession.addPasswordIdentity(EncryptionTool.getInstance().decrypt(password)); + } + if(keyFile != null) { + KeyPairProvider keyPairProvider = new FileKeyPairProvider(new String[]{keyFile}); + KeyPair keyPair = keyPairProvider.loadKeys().iterator().next(); + clientSession.addPublicKeyIdentity(keyPair); + } + AuthFuture authFuture = clientSession.auth(); + authFuture.await(AUTH_TIMEOUT); + if(!authFuture.isSuccess()) { + throw new SshException("Error establishing ssh connection to [" + username + "@" + host + ":" + port + "]. Authentication failed."); + } + } catch(RuntimeException e) { + throw e; + } catch(Exception e) { + throw new SshException("Error establishing ssh connection to [" + username + "@" + host + ":" + port + "].", e); + } + if(logger.isDebugEnabled()) { + logger.debug("SSH: connected to [" + toString() + "]"); + } + } + + @Override + public void disconnect() { + try { + if(logger.isDebugEnabled()) { + logger.debug("SSH: disconnecting from [" + toString() + "]"); + } + clientSession.close(false); + } finally { + if(sshClient != null) { + sshClient.stop(); + } + } + } + + @Override + public void setExecTimeout(long timeout) { + this.timeout = timeout; + } + + @Override + public int execCommand(String cmd, OutputStream out, OutputStream err) { + return execCommand(cmd, out, err, false); + } + + @Override + public int execCommandWithPty(String cmd, OutputStream out) { + return execCommand(cmd, out, out, true); + } + + private int execCommand(String cmd, OutputStream out, OutputStream err, boolean usePty) { + try { + if(logger.isDebugEnabled()) { + logger.debug("SSH: executing command"); + } + ChannelExec client = clientSession.createExecChannel(cmd); + client.setUsePty(usePty); // use pseudo-tty? + client.setOut(out); + client.setErr(err); + OpenFuture openFuture = client.open(); + int exitStatus = 0; + try { + client.waitFor(ClientChannel.CLOSED, timeout); + openFuture.verify(); + Integer exitStatusI = client.getExitStatus(); + if(exitStatusI == null) { + throw new SshException("Error executing command [" + cmd + "] over SSH [" + username + "@" + host + ":" + port + "]. Operation timed out."); + } + exitStatus = exitStatusI; + } finally { + client.close(false); + } + return exitStatus; + } catch(RuntimeException e) { + throw e; + } catch(Exception t) { + throw new SshException("Error executing command [" + cmd + "] over SSH [" + username + "@" + host + ":" + port + "]", t); + } + } + + @Override + public String toString() { + String address = host; + if(username != null) { + address = username + '@' +address; + } + return address; + } +} diff --git a/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshdDataAccessService.java b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshdDataAccessService.java new file mode 100644 index 000000000..a12e2be93 --- /dev/null +++ b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/java/org/openecomp/appc/adapter/ssh/sshd/SshdDataAccessService.java @@ -0,0 +1,106 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.adapter.ssh.sshd; + +import javax.sql.rowset.CachedRowSet; + +import org.openecomp.appc.adapter.ssh.Constants; +import org.openecomp.appc.adapter.ssh.SshConnectionDetails; +import org.openecomp.appc.adapter.ssh.SshDataAccessException; +import org.openecomp.appc.adapter.ssh.SshDataAccessService; +import org.openecomp.appc.exceptions.APPCException; +import org.openecomp.sdnc.sli.resource.dblib.DbLibService; + +import java.sql.SQLException; +import java.util.ArrayList; + + + +public class SshdDataAccessService implements SshDataAccessService { + + private String schema = Constants.NETCONF_SCHEMA; + private DbLibService dbLibService; + + @Override + public void setSchema(String schema) { + this.schema = schema; + } + + @Override + public void setDbLibService(DbLibService dbLibService) { + this.dbLibService = dbLibService; + } + + @Override + public boolean retrieveConnectionDetails(String vnfType, SshConnectionDetails connectionDetails) throws SshDataAccessException { + + boolean recordFound = false; + + String queryString = "select " + Constants.USER_NAME_TABLE_FIELD_NAME + "," + Constants.PASSWORD_TABLE_FIELD_NAME + "," + Constants.PORT_NUMBER_TABLE_FIELD_NAME + " " + + "from " + Constants.DEVICE_AUTHENTICATION_TABLE_NAME + " " + + "where " + Constants.VNF_TYPE_TABLE_FIELD_NAME + " = ?"; + + ArrayList<String> argList = new ArrayList<>(); + argList.add(vnfType); + + try { + + final CachedRowSet data = dbLibService.getData(queryString, argList, schema); + if (data.first()) { + recordFound = true; + connectionDetails.setUsername(data.getString(Constants.USER_NAME_TABLE_FIELD_NAME)); + connectionDetails.setPassword(data.getString(Constants.PASSWORD_TABLE_FIELD_NAME)); + connectionDetails.setPort(data.getInt(Constants.PORT_NUMBER_TABLE_FIELD_NAME)); + } + + } catch (SQLException e) { + throw new SshDataAccessException(e); + } + + return recordFound; + } + + @Override + public String retrieveConfigFileName(String xmlID) throws SshDataAccessException { + String fileContent; + + String queryString = "select " + Constants.FILE_CONTENT_TABLE_FIELD_NAME + " " + + "from " + Constants.CONFIGFILES_TABLE_NAME + " " + + "where " + Constants.FILE_NAME_TABLE_FIELD_NAME + " = ?"; + + ArrayList<String> argList = new ArrayList<>(); + argList.add(xmlID); + + try { + + final CachedRowSet data = dbLibService.getData(queryString, argList, schema); + fileContent = data.getString(Constants.FILE_CONTENT_TABLE_FIELD_NAME); + + } catch (SQLException e) { + throw new SshDataAccessException(e); + } + + return fileContent; + } + + +} diff --git a/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..f9d10868a --- /dev/null +++ b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============LICENSE_START======================================================= + openECOMP : APP-C + ================================================================================ + Copyright (C) 2017 AT&T Intellectual Property. All rights + reserved. + ================================================================================ + Licensed 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. + ============LICENSE_END========================================================= + --> + + +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd"> + + <bean id="sshdBean" class="org.openecomp.appc.adapter.ssh.sshd.SshAdapterSshd" scope="singleton"/> + <service id="sshAdapter" interface="org.openecomp.appc.adapter.ssh.SshAdapter" ref="sshdBean"/> + + <reference id="dbLibServiceRef" availability="mandatory" activation="eager" interface="org.openecomp.sdnc.sli.resource.dblib.DbLibService" /> + <bean id="sshdDAServiceBean" class="org.openecomp.appc.adapter.ssh.sshd.SshdDataAccessService" scope="singleton"> + <property name="dbLibService" ref="dbLibServiceRef" /> + </bean> + + <service id="sshDAService" interface="org.openecomp.appc.adapter.ssh.SshDataAccessService" ref="sshdDAServiceBean"/> + +</blueprint> diff --git a/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/test/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterSample.java b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/test/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterSample.java new file mode 100644 index 000000000..197915de8 --- /dev/null +++ b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/test/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterSample.java @@ -0,0 +1,56 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.adapter.ssh.sshd; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; + +import org.openecomp.appc.adapter.ssh.SshAdapter; +import org.openecomp.appc.adapter.ssh.SshConnection; +import org.openecomp.appc.adapter.ssh.sshd.SshAdapterSshd; + +public class SshAdapterSample { + + public static void main(String[] args) { + String host = "hostname"; + int port = 22; + String username = "user"; + String password = "secret"; + String command = "ls"; + + SshAdapter sshAdapter = new SshAdapterSshd(); + SshConnection sshConnection = sshAdapter.getConnection(host, port, username, password); + sshConnection.connect(); + try { + OutputStream stdout = new ByteArrayOutputStream(); + OutputStream stderr = new ByteArrayOutputStream(); + int status = sshConnection.execCommand(command, stdout, stderr); + if(status == 0) { + System.out.println("Command executed successfully. Output:\n" + stdout.toString()); + } else { + System.err.println("Command returned status " + status + ". Error:\n" + stderr.toString()); + } + } finally { + sshConnection.disconnect(); + } + } +} diff --git a/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/test/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterTest.java b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/test/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterTest.java new file mode 100644 index 000000000..1d7d06df2 --- /dev/null +++ b/appc-adapters/appc-ssh-adapter/appc-ssh-adapter-sshd/src/test/java/org/openecomp/appc/adapter/ssh/sshd/SshAdapterTest.java @@ -0,0 +1,247 @@ +/*- + * ============LICENSE_START======================================================= + * openECOMP : APP-C + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights + * reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.appc.adapter.ssh.sshd; + +import org.apache.sshd.SshServer; +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.util.OsUtils; +import org.apache.sshd.server.Command; +import org.apache.sshd.server.CommandFactory; +import org.apache.sshd.server.PasswordAuthenticator; +import org.apache.sshd.server.PublickeyAuthenticator; +import org.apache.sshd.server.command.ScpCommandFactory; +import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; +import org.apache.sshd.server.session.ServerSession; +import org.apache.sshd.server.sftp.SftpSubsystem; +import org.apache.sshd.server.shell.ProcessShellFactory; +import org.hamcrest.CoreMatchers; +import org.junit.*; +import org.junit.rules.ExpectedException; +import org.openecomp.appc.adapter.ssh.SshAdapter; +import org.openecomp.appc.adapter.ssh.SshConnection; +import org.openecomp.appc.adapter.ssh.SshException; +import org.openecomp.appc.adapter.ssh.sshd.SshAdapterSshd; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.BindException; +import java.security.PublicKey; +import java.util.Collections; +import java.util.EnumSet; + +public class SshAdapterTest { + + private static final boolean START_SERVER = true; + private static final String SSH_HOST = "localhost"; + private static final int SSH_PORT = 2222; + private static final String SSH_USERNAME = "test"; + private static final String SSH_PASSWORD = "test"; + private static final String F_TEST_CMD = "ping -%c 4 %s"; + + private int sshPort = SSH_PORT; + private SshServer sshd; + private SshAdapter sshAdapter = new SshAdapterSshd(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testExecute() { + String cmd = String.format(F_TEST_CMD, OsUtils.isUNIX() ? 'c' : 'n', "localhost"); + SshConnection sshConnection = connect(SSH_USERNAME, SSH_PASSWORD); + try { + System.out.println("SSH client connected. Server port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]"); + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + int status = execCmd(sshConnection, cmd, stdout, stderr, false); + Assert.assertEquals(stdout.toString() + ". " + stderr.toString(), 0, status); + } finally { + disconnect(sshConnection); + } + } + + @Test + public void testExecuteWithPty() { + String cmd = String.format(F_TEST_CMD, OsUtils.isUNIX() ? 'c' : 'n', "localhost"); + SshConnection sshConnection = connect(SSH_USERNAME, SSH_PASSWORD); + try { + System.out.println("SSH client connected. Server port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]"); + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + int status = execCmd(sshConnection, cmd, stdout, null, true); + Assert.assertEquals(stdout.toString() + ". " + stdout.toString(), 0, status); + } finally { + disconnect(sshConnection); + } + } + + @Test + public void testExecuteInvalidCommand() { + String cmd = String.format(F_TEST_CMD, OsUtils.isUNIX() ? 'c' : 'n', "nosuchhost"); + SshConnection sshConnection = connect(SSH_USERNAME, SSH_PASSWORD); + try { + ByteArrayOutputStream stdout = new ByteArrayOutputStream(); + ByteArrayOutputStream stderr = new ByteArrayOutputStream(); + int status = execCmd(sshConnection, cmd, stdout, stderr, false); + Assert.assertNotEquals(stdout.toString() + ". " + stderr.toString(), 0, status); + } finally { + disconnect(sshConnection); + } + } + + @Test + public void testWrongUsername() { + thrown.expect(SshException.class); + thrown.expectMessage(CoreMatchers.containsString("Authentication failed")); + disconnect(connect("WrongUsername", SSH_PASSWORD)); + } + + @Test + public void testWrongPassword() { + thrown.expect(SshException.class); + thrown.expectMessage(CoreMatchers.containsString("Authentication failed")); + disconnect(connect(SSH_USERNAME, "WrongPassword")); + } + + @Before + public void beforeTest() throws IOException { + if (START_SERVER) { + startServer(); + } + } + + @After + public void afterTest() throws InterruptedException { + stopServer(); + } + + private SshConnection connect(String username, String password) { + SshConnection sshConnection = sshAdapter.getConnection(SSH_HOST, sshPort, username, password); + sshConnection.connect(); + System.out.println("SSH client connected. Server port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]"); + return sshConnection; + } + + private void disconnect(SshConnection sshConnection) { + sshConnection.disconnect(); + System.out.println("SSH client disconnected. Server port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]"); + } + + private int execCmd(SshConnection sshConnection, String cmd, OutputStream stdout, OutputStream stderr, boolean usePty) { + System.out.println("=> Running command [" + cmd + "] over SSH"); + int status; + if (usePty) { + status = sshConnection.execCommandWithPty(cmd, stdout); + } else { + status = sshConnection.execCommand(cmd, stdout, stderr); + } + System.out.println("=> Command [" + cmd + "] status is [" + status + "], stdout is [" + String.valueOf(stdout) + "], stderr is [" + String.valueOf(stderr) + "]"); + return status; + } + + private void startServer() throws IOException { + sshd = SshServer.setUpDefaultServer(); + sshd.setSubsystemFactories(Collections.<NamedFactory<Command>>singletonList(new SftpSubsystem.Factory())); + sshd.setCommandFactory(new ScpCommandFactory(new CommandFactory() { + + public Command createCommand(String command) { + EnumSet<ProcessShellFactory.TtyOptions> ttyOptions; + if (OsUtils.isUNIX()) { + ttyOptions = EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr); + } else { + ttyOptions = EnumSet.of(ProcessShellFactory.TtyOptions.Echo, ProcessShellFactory.TtyOptions.ICrNl, ProcessShellFactory.TtyOptions.ONlCr); + } + return new ProcessShellFactory(command.split(" "), ttyOptions).create(); + } + })); + if (OsUtils.isUNIX()) { + sshd.setShellFactory(new ProcessShellFactory(new String[]{"/bin/sh", "-i", "-l"}, + EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr))); + } else { + sshd.setShellFactory(new ProcessShellFactory(new String[]{"cmd.exe "}, + EnumSet.of(ProcessShellFactory.TtyOptions.Echo, ProcessShellFactory.TtyOptions.ICrNl, ProcessShellFactory.TtyOptions.ONlCr))); + } +// if(SecurityUtils.isBouncyCastleRegistered()) { +// sshd.setKeyPairProvider(new PEMGeneratorHostKeyProvider(System.getProperty("java.io.tmpdir") + "/key.pem")); +// } else { + sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(System.getProperty("java.io.tmpdir") + "/key.ser")); +// } + sshd.setPasswordAuthenticator(new PasswordAuthenticator() { + + @Override + public boolean authenticate(String username, String password, ServerSession session) { + return (SSH_USERNAME.equals(username) && SSH_PASSWORD.equals(password)); + } + }); + sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() { + + public boolean authenticate(String username, PublicKey key, ServerSession session) { + return true; + } + }); + sshd.getProperties().put(SshServer.WELCOME_BANNER, "Welcome to SSHD\n"); + startServer0(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // ignore + } + } + + private void startServer0() throws IOException { + boolean serverStarted = false; + IOException exception = null; + while (!serverStarted && (sshPort < Integer.MAX_VALUE)) { + try { + System.out.println("Starting SSH server on port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]"); + sshd.setPort(sshPort); + sshd.start(); + serverStarted = true; + } catch (BindException e) { + System.err.println("Cannot start SSH server on port [" + sshPort + "]. " + e.getMessage()); + if (exception == null) { + // store first thrown exception - will be thrown if cannot start the server + exception = e; + } + sshPort++; + } + } + if (!serverStarted) { + throw exception; + } + System.out.println("SSH server started on port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]"); + } + + private void stopServer() { + try { + if (sshd != null) { + sshd.stop(true); + System.out.println("SSH server stopped on port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]"); + } + } catch (InterruptedException e) { + System.err.println("=> Error stopping SSH server."); + e.printStackTrace(); + } finally { + sshd = null; + } + } +} |