| /******************************************************************************* |
| * Copyright (c) 2011 SAP AG |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Lazar Kirchev, SAP AG - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.console.ssh; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.InputStream; |
| import java.io.PrintStream; |
| import java.io.PrintWriter; |
| import java.io.StringBufferInputStream; |
| import java.util.Collection; |
| import java.util.Dictionary; |
| import java.util.HashMap; |
| import java.util.Hashtable; |
| import java.util.Map; |
| |
| import org.apache.felix.service.command.CommandProcessor; |
| import org.apache.felix.service.command.CommandSession; |
| import org.apache.sshd.ClientChannel; |
| import org.apache.sshd.ClientSession; |
| import org.apache.sshd.SshClient; |
| import org.apache.sshd.client.future.ConnectFuture; |
| import org.apache.sshd.client.future.DefaultConnectFuture; |
| import org.apache.sshd.common.RuntimeSshException; |
| import org.apache.sshd.server.Environment; |
| import org.easymock.EasyMock; |
| import org.easymock.IAnswer; |
| import org.eclipse.equinox.console.common.ConsoleInputStream; |
| import org.eclipse.equinox.console.storage.DigestUtil; |
| import org.eclipse.equinox.console.storage.SecureUserStore; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleException; |
| import org.osgi.framework.BundleListener; |
| import org.osgi.framework.Filter; |
| import org.osgi.framework.FrameworkListener; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceListener; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.service.cm.ManagedService; |
| |
| |
| public class SshCommandWithConfigAdminTests { |
| private static final int TEST_CONTENT = 100; |
| private static final String USER_STORE_FILE_NAME = "org.eclipse.equinox.console.jaas.file"; |
| private static final String JAAS_CONFIG_FILE_NAME = "jaas.config"; |
| private static final String JAAS_CONFIG_PROPERTY_NAME = "java.security.auth.login.config"; |
| private static final String DEFAULT_USER_STORAGE = "osgi.console.ssh.useDefaultSecureStorage"; |
| private static final String STORE_FILE_NAME = SshCommandTests.class.getName() + "_store"; |
| private static final String GOGO_SHELL_COMMAND = "gosh --login --noshutdown"; |
| private static final String TRUE = "true"; |
| private static final String FALSE = "false"; |
| private static final String USERNAME = "username"; |
| private static final String PASSWORD = "password"; |
| private static final String STOP_COMMAND = "stop"; |
| private static final String TERM_PROPERTY = "TERM"; |
| private static final String XTERM = "XTERM"; |
| private static final String HOST = "localhost"; |
| private static final String SSH_PORT = "2222"; |
| private static final long WAIT_TIME = 5000; |
| private static final String USE_CONFIG_ADMIN_PROP = "osgi.console.useConfigAdmin"; |
| private ManagedService configurator; |
| |
| @Before |
| public void init() throws Exception { |
| clean(); |
| initStore(); |
| initJaasConfigFile(); |
| } |
| |
| @Test |
| public void testSshCommandWithConfigAdmin() throws Exception { |
| |
| CommandSession session = EasyMock.createMock(CommandSession.class); |
| EasyMock.makeThreadSafe(session, true); |
| session.put((String)EasyMock.anyObject(), EasyMock.anyObject()); |
| EasyMock.expectLastCall().times(5); |
| EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null); |
| session.close(); |
| EasyMock.expectLastCall(); |
| EasyMock.replay(session); |
| |
| CommandProcessor processor = EasyMock.createMock(CommandProcessor.class); |
| EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session); |
| EasyMock.replay(processor); |
| |
| final ServiceRegistration<?> registration = EasyMock.createMock(ServiceRegistration.class); |
| registration.setProperties((Dictionary)EasyMock.anyObject()); |
| EasyMock.expectLastCall(); |
| EasyMock.replay(registration); |
| |
| BundleContext context = EasyMock.createMock(BundleContext.class); |
| EasyMock.makeThreadSafe(context, true); |
| EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(TRUE); |
| EasyMock.expect(context.getProperty(DEFAULT_USER_STORAGE)).andReturn(TRUE).anyTimes(); |
| EasyMock.expect( |
| (ServiceRegistration) context.registerService( |
| (String)EasyMock.anyObject(), |
| (ManagedService)EasyMock.anyObject(), |
| (Dictionary<String, ?>)EasyMock.anyObject()) |
| ).andAnswer((IAnswer<ServiceRegistration<?>>) new IAnswer<ServiceRegistration<?>>() { |
| public ServiceRegistration<?> answer() { |
| configurator = (ManagedService) EasyMock.getCurrentArguments()[1]; |
| return registration; |
| } |
| }); |
| EasyMock.expect( |
| context.registerService( |
| (String)EasyMock.anyObject(), |
| (SshCommand)EasyMock.anyObject(), |
| (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null); |
| EasyMock.replay(context); |
| |
| Map<String, String> environment = new HashMap<String, String>(); |
| environment.put(TERM_PROPERTY, XTERM); |
| Environment env = EasyMock.createMock(Environment.class); |
| EasyMock.expect(env.getEnv()).andReturn(environment); |
| EasyMock.replay(env); |
| |
| SshCommand command = new SshCommand(processor, context); |
| Dictionary props = new Hashtable(); |
| props.put("port", SSH_PORT); |
| props.put("host", HOST); |
| props.put("enabled", TRUE); |
| configurator.updated(props); |
| |
| SshClient client = SshClient.setUpDefaultClient(); |
| client.start(); |
| try { |
| ConnectFuture connectFuture = client.connect(HOST, Integer.valueOf(SSH_PORT)); |
| DefaultConnectFuture defaultConnectFuture = (DefaultConnectFuture) connectFuture; |
| |
| try { |
| Thread.sleep(WAIT_TIME); |
| } catch (InterruptedException ie) { |
| // do nothing |
| } |
| ClientSession sshSession = defaultConnectFuture.getSession(); |
| |
| int ret = ClientSession.WAIT_AUTH; |
| sshSession.authPassword(USERNAME, PASSWORD); |
| ret = sshSession.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0); |
| |
| if ((ret & ClientSession.CLOSED) != 0) { |
| System.err.println("error"); |
| System.exit(-1); |
| } |
| ClientChannel channel = sshSession.createChannel("shell"); |
| channel.setIn(new StringBufferInputStream(TEST_CONTENT + "\n")); |
| ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); |
| channel.setOut(byteOut); |
| channel.setErr(byteOut); |
| channel.open(); |
| try { |
| Thread.sleep(WAIT_TIME); |
| } catch (InterruptedException ie) { |
| // do nothing |
| } |
| byte[] output = byteOut.toByteArray(); |
| Assert.assertEquals("Output not as expected",Integer.toString(TEST_CONTENT), new String(output).trim()); |
| sshSession.close(true); |
| } finally { |
| client.stop(); |
| } |
| |
| command.ssh(new String[] {STOP_COMMAND}); |
| return; |
| } |
| |
| @Test |
| public void testSshCommandWithConfigAdminDisabledSsh() throws Exception { |
| testDisabled(false); |
| } |
| |
| @Test |
| public void testSshCommandWithConfigAdminDisabledSshByDefault() throws Exception { |
| testDisabled(true); |
| } |
| |
| private void testDisabled(boolean isDefault) throws Exception { |
| CommandSession session = EasyMock.createMock(CommandSession.class); |
| session.put((String)EasyMock.anyObject(), EasyMock.anyObject()); |
| EasyMock.expectLastCall().times(4); |
| EasyMock.expect(session.execute(GOGO_SHELL_COMMAND)).andReturn(null); |
| session.close(); |
| EasyMock.expectLastCall(); |
| EasyMock.replay(session); |
| |
| CommandProcessor processor = EasyMock.createMock(CommandProcessor.class); |
| EasyMock.expect(processor.createSession((ConsoleInputStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject(), (PrintStream)EasyMock.anyObject())).andReturn(session); |
| EasyMock.replay(processor); |
| |
| final ServiceRegistration<?> registration = EasyMock.createMock(ServiceRegistration.class); |
| registration.setProperties((Dictionary)EasyMock.anyObject()); |
| EasyMock.expectLastCall(); |
| EasyMock.replay(registration); |
| |
| BundleContext context = EasyMock.createMock(BundleContext.class); |
| EasyMock.expect(context.getProperty(USE_CONFIG_ADMIN_PROP)).andReturn(TRUE); |
| EasyMock.expect(context.getProperty(DEFAULT_USER_STORAGE)).andReturn(TRUE).anyTimes(); |
| EasyMock.expect( |
| (ServiceRegistration) context.registerService( |
| (String)EasyMock.anyObject(), |
| (ManagedService)EasyMock.anyObject(), |
| (Dictionary<String, ?>)EasyMock.anyObject()) |
| ).andAnswer((IAnswer<ServiceRegistration<?>>) new IAnswer<ServiceRegistration<?>>() { |
| public ServiceRegistration<?> answer() { |
| configurator = (ManagedService) EasyMock.getCurrentArguments()[1]; |
| return registration; |
| } |
| }); |
| EasyMock.expect( |
| context.registerService( |
| (String)EasyMock.anyObject(), |
| (SshCommand)EasyMock.anyObject(), |
| (Dictionary<String, ?>)EasyMock.anyObject())).andReturn(null); |
| EasyMock.replay(context); |
| |
| Map<String, String> environment = new HashMap<String, String>(); |
| environment.put(TERM_PROPERTY, XTERM); |
| Environment env = EasyMock.createMock(Environment.class); |
| EasyMock.expect(env.getEnv()).andReturn(environment); |
| EasyMock.replay(env); |
| |
| SshCommand command = new SshCommand(processor, context); |
| Dictionary props = new Hashtable(); |
| props.put("port", SSH_PORT); |
| props.put("host", HOST); |
| if (isDefault == false) { |
| props.put("enabled", FALSE); |
| } |
| configurator.updated(props); |
| |
| SshClient client = SshClient.setUpDefaultClient(); |
| client.start(); |
| try { |
| ConnectFuture connectFuture = client.connect(HOST, Integer.valueOf(SSH_PORT)); |
| DefaultConnectFuture defaultConnectFuture = (DefaultConnectFuture) connectFuture; |
| |
| try { |
| Thread.sleep(WAIT_TIME); |
| } catch (InterruptedException ie) { |
| // do nothing |
| } |
| ClientSession sshSession; |
| try { |
| sshSession = defaultConnectFuture.getSession(); |
| Assert.fail("It should not be possible to connect to " + HOST + ":" + SSH_PORT); |
| } catch (RuntimeSshException e) { |
| //this is expected |
| } |
| } finally { |
| client.stop(); |
| } |
| |
| try { |
| command.ssh(new String[] {STOP_COMMAND}); |
| } catch (IllegalStateException e) { |
| // this is expected |
| } |
| return; |
| } |
| |
| @After |
| public void cleanUp() { |
| clean(); |
| } |
| |
| private void clean() { |
| System.setProperty(USER_STORE_FILE_NAME, ""); |
| File file = new File(STORE_FILE_NAME); |
| if (file.exists()) { |
| file.delete(); |
| } |
| |
| System.setProperty(JAAS_CONFIG_PROPERTY_NAME, ""); |
| File jaasConfFile = new File(JAAS_CONFIG_FILE_NAME); |
| if (jaasConfFile.exists()) { |
| jaasConfFile.delete(); |
| } |
| } |
| |
| private void initStore() throws Exception { |
| System.setProperty(USER_STORE_FILE_NAME, STORE_FILE_NAME); |
| SecureUserStore.initStorage(); |
| SecureUserStore.putUser(USERNAME, DigestUtil.encrypt(PASSWORD), null); |
| } |
| |
| private void initJaasConfigFile() throws Exception { |
| System.setProperty(JAAS_CONFIG_PROPERTY_NAME, JAAS_CONFIG_FILE_NAME); |
| File jaasConfFile = new File(JAAS_CONFIG_FILE_NAME); |
| if (!jaasConfFile.exists()) { |
| PrintWriter out = null; |
| try { |
| out = new PrintWriter(jaasConfFile); |
| out.println("equinox_console {"); |
| out.println(" org.eclipse.equinox.console.jaas.SecureStorageLoginModule REQUIRED;"); |
| out.println("};"); |
| } finally { |
| if (out != null) { |
| out.close(); |
| } |
| } |
| } |
| } |
| |
| } |