2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * ================================================================================
9 * Modifications Copyright (C) 2019 Ericsson
10 * =============================================================================
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
23 * ============LICENSE_END=========================================================
26 package org.onap.appc.adapter.ssh.sshd;
28 import org.apache.sshd.server.SshServer;
29 import org.apache.sshd.common.NamedFactory;
30 import org.apache.sshd.common.util.OsUtils;
31 import org.apache.sshd.server.command.Command;
32 import org.apache.sshd.server.auth.password.PasswordAuthenticator;
33 import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
34 import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
35 import org.apache.sshd.server.scp.ScpCommandFactory;
36 import org.apache.sshd.server.session.ServerSession;
37 import org.apache.sshd.server.shell.ProcessShellFactory;
38 import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
39 import org.hamcrest.CoreMatchers;
40 import org.junit.After;
41 import org.junit.Assert;
42 import org.junit.Before;
43 import org.junit.Rule;
44 import org.junit.Test;
45 import org.junit.rules.ExpectedException;
46 import org.mockito.Mockito;
47 import org.onap.appc.adapter.ssh.SshAdapter;
48 import org.onap.appc.adapter.ssh.SshConnection;
49 import org.onap.appc.adapter.ssh.SshException;
50 import org.powermock.reflect.Whitebox;
51 import static org.junit.Assert.assertEquals;
52 import static org.junit.Assert.assertTrue;
53 import java.io.ByteArrayOutputStream;
55 import java.io.IOException;
56 import java.io.OutputStream;
57 import java.net.BindException;
58 import java.security.PublicKey;
59 import java.util.Collections;
62 public class SshAdapterTest {
64 private static final boolean START_SERVER = true;
65 private static final String SSH_HOST = "localhost";
66 private static final int SSH_PORT = 2222;
67 private static final String SSH_USERNAME = "test";
68 private static final String SSH_PASSWORD = "test";
69 private static final String F_TEST_CMD = "ping -%c 4 %s";
71 private int sshPort = SSH_PORT;
72 private SshServer sshd;
73 private SshAdapter sshAdapter = new SshAdapterSshd();
76 public ExpectedException thrown = ExpectedException.none();
79 public void testExecute() {
80 String cmd = String.format(F_TEST_CMD, OsUtils.isUNIX() ? 'c' : 'n', "localhost");
81 SshConnection sshConnection = connect(SSH_USERNAME, SSH_PASSWORD);
83 System.out.println("SSH client connected. Server port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]");
84 ByteArrayOutputStream stdout = new ByteArrayOutputStream();
85 ByteArrayOutputStream stderr = new ByteArrayOutputStream();
86 int status = execCmd(sshConnection, cmd, stdout, stderr, false);
87 Assert.assertEquals(stdout.toString() + ". " + stderr.toString(), 0, status);
89 disconnect(sshConnection);
94 public void testExecuteWithPty() {
95 String cmd = String.format(F_TEST_CMD, OsUtils.isUNIX() ? 'c' : 'n', "localhost");
96 SshConnection sshConnection = connect(SSH_USERNAME, SSH_PASSWORD);
98 System.out.println("SSH client connected. Server port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]");
99 ByteArrayOutputStream stdout = new ByteArrayOutputStream();
100 int status = execCmd(sshConnection, cmd, stdout, null, true);
101 Assert.assertEquals(stdout.toString() + ". " + stdout.toString(), 0, status);
103 disconnect(sshConnection);
108 public void testExecuteInvalidCommand() {
109 String cmd = String.format(F_TEST_CMD, OsUtils.isUNIX() ? 'c' : 'n', "nosuchhost");
110 SshConnection sshConnection = connect(SSH_USERNAME, SSH_PASSWORD);
112 ByteArrayOutputStream stdout = new ByteArrayOutputStream();
113 ByteArrayOutputStream stderr = new ByteArrayOutputStream();
114 int status = execCmd(sshConnection, cmd, stdout, stderr, false);
115 Assert.assertNotEquals(stdout.toString() + ". " + stderr.toString(), 0, status);
117 disconnect(sshConnection);
122 public void testWrongUsername() {
123 thrown.expect(SshException.class);
124 thrown.expectMessage(CoreMatchers.containsString("Authentication failed"));
125 disconnect(connect("WrongUsername", SSH_PASSWORD));
129 public void testWrongPassword() {
130 thrown.expect(SshException.class);
131 thrown.expectMessage(CoreMatchers.containsString("Authentication failed"));
132 disconnect(connect(SSH_USERNAME, "WrongPassword"));
136 public void testInstantiateWithKeyFile() {
137 SshConnection connection = sshAdapter.getConnection(null, 0, null);
138 assertTrue(connection instanceof SshConnection);
142 public void testConnectWithRetry() {
143 SshConnection connection = Mockito.spy(sshAdapter.getConnection(
144 SSH_HOST, SSH_PORT, SSH_USERNAME, SSH_PASSWORD));
145 connection.setExecTimeout(1);
146 connection.connectWithRetry();
147 Mockito.verify(connection).connect();
151 public void testToString() {
152 SshConnection connection = Mockito.spy(sshAdapter.getConnection(
153 SSH_HOST, SSH_PORT, SSH_USERNAME, SSH_PASSWORD));
154 assertEquals(SSH_USERNAME + "@" + SSH_HOST, connection.toString());
156 Whitebox.setInternalState(connection, "username", s);
157 assertEquals(SSH_HOST, connection.toString());
161 public void beforeTest() throws IOException {
168 public void afterTest() throws InterruptedException {
172 private SshConnection connect(String username, String password) {
173 SshConnection sshConnection = sshAdapter.getConnection(SSH_HOST, sshPort, username, password);
174 sshConnection.connect();
175 System.out.println("SSH client connected. Server port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]");
176 return sshConnection;
179 private void disconnect(SshConnection sshConnection) {
180 sshConnection.disconnect();
181 System.out.println("SSH client disconnected. Server port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]");
184 private int execCmd(SshConnection sshConnection, String cmd, OutputStream stdout, OutputStream stderr, boolean usePty) {
185 System.out.println("=> Running command [" + cmd + "] over SSH");
188 status = sshConnection.execCommandWithPty(cmd, stdout);
190 status = sshConnection.execCommand(cmd, stdout, stderr);
192 System.out.println("=> Command [" + cmd + "] status is [" + status + "], stdout is [" + String.valueOf(stdout) + "], stderr is [" + String.valueOf(stderr) + "]");
196 private void startServer() throws IOException {
197 sshd = SshServer.setUpDefaultServer();
198 sshd.setSubsystemFactories(Collections.<NamedFactory<Command>>singletonList(new SftpSubsystemFactory()));
199 sshd.setCommandFactory(new ScpCommandFactory() {
201 public Command createCommand(String command) {
202 //EnumSet<ProcessShellFactory.TtyOptions> ttyOptions;
203 //if (OsUtils.isUNIX()) {
204 // ttyOptions = EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr);
206 // ttyOptions = EnumSet.of(ProcessShellFactory.TtyOptions.Echo, ProcessShellFactory.TtyOptions.ICrNl, ProcessShellFactory.TtyOptions.ONlCr);
208 //return new ProcessShellFactory(command.split(" "), ttyOptions).create();
210 return new ProcessShellFactory(command.split(" ")).create();
213 if (OsUtils.isUNIX()) {
214 sshd.setShellFactory(new ProcessShellFactory(new String[]{"/bin/sh", "-i", "-l"}/*,
215 EnumSet.of(ProcessShellFactory.TtyOptions.ONlCr)*/));
217 sshd.setShellFactory(new ProcessShellFactory(new String[]{"cmd.exe "}/*,
218 EnumSet.of(ProcessShellFactory.TtyOptions.Echo, ProcessShellFactory.TtyOptions.ICrNl, ProcessShellFactory.TtyOptions.ONlCr)*/));
220 // if(SecurityUtils.isBouncyCastleRegistered()) {
221 // sshd.setKeyPairProvider(new PEMGeneratorHostKeyProvider(System.getProperty("java.io.tmpdir") + "/key.pem"));
223 sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File(System.getProperty("java.io.tmpdir") + "/key.ser")));
225 sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
227 public boolean authenticate(String username, String password, ServerSession session) {
228 return (SSH_USERNAME.equals(username) && SSH_PASSWORD.equals(password));
231 sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() {
232 // We're testing access using passwords, so do not authorize authentitication using public keys
233 public boolean authenticate(String username, PublicKey key, ServerSession session) {
237 sshd.getProperties().put(SshServer.WELCOME_BANNER, "Welcome to SSHD\n");
241 } catch (InterruptedException e) {
246 private void startServer0() throws IOException {
247 boolean serverStarted = false;
248 IOException exception = null;
249 while (!serverStarted && (sshPort < Integer.MAX_VALUE)) {
251 System.out.println("Starting SSH server on port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]");
252 sshd.setPort(sshPort);
254 serverStarted = true;
255 } catch (BindException e) {
256 System.err.println("Cannot start SSH server on port [" + sshPort + "]. " + e.getMessage());
257 if (exception == null) {
258 // store first thrown exception - will be thrown if cannot start the server
264 if (!serverStarted) {
267 System.out.println("SSH server started on port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]");
270 private void stopServer() {
274 System.out.println("SSH server stopped on port [" + sshPort + "]. [" + getClass().getName() + "#" + System.identityHashCode(this) + "]");
276 } catch (IOException e) {
277 System.err.println("=> IO Error stopping SSH server.");