| /** |
| * 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.camel.component.scp; |
| |
| import java.io.IOException; |
| import java.security.Provider; |
| import java.security.Provider.Service; |
| import java.security.PublicKey; |
| import java.security.Security; |
| import java.util.Arrays; |
| |
| import com.jcraft.jsch.JSch; |
| import com.jcraft.jsch.JSchException; |
| import com.jcraft.jsch.Session; |
| import com.jcraft.jsch.UserInfo; |
| |
| import org.apache.camel.test.AvailablePortFinder; |
| import org.apache.camel.test.junit4.CamelTestSupport; |
| import org.apache.sshd.SshServer; |
| import org.apache.sshd.common.NamedFactory; |
| import org.apache.sshd.common.keyprovider.FileKeyPairProvider; |
| import org.apache.sshd.server.Command; |
| import org.apache.sshd.server.PasswordAuthenticator; |
| import org.apache.sshd.server.PublickeyAuthenticator; |
| import org.apache.sshd.server.command.ScpCommandFactory; |
| import org.apache.sshd.server.session.ServerSession; |
| import org.apache.sshd.server.sftp.SftpSubsystem; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public abstract class ScpServerTestSupport extends CamelTestSupport { |
| protected static final Logger LOG = LoggerFactory.getLogger(ScpServerTestSupport.class); |
| protected static final String SCP_ROOT_DIR = "target/test-classes/scp"; |
| protected static final String KNOWN_HOSTS = "known_hosts"; |
| protected static int port; |
| |
| private boolean acceptLocalhostConnections = true; |
| private String knownHostsFile; |
| |
| private boolean setupComplete; |
| private SshServer sshd; |
| |
| protected ScpServerTestSupport() { |
| this(true); |
| } |
| |
| protected ScpServerTestSupport(boolean acceptLocalhostConnections) { |
| this.acceptLocalhostConnections = acceptLocalhostConnections; |
| } |
| |
| protected int getPort() { |
| return port; |
| } |
| |
| protected SshServer getSshd() { |
| return sshd; |
| } |
| |
| @BeforeClass |
| public static void initPort() throws Exception { |
| port = AvailablePortFinder.getNextAvailable(21000); |
| } |
| |
| @Override |
| @Before |
| public void setUp() throws Exception { |
| deleteDirectory(getScpPath()); |
| createDirectory(getScpPath()); |
| |
| setupComplete = startSshd(); |
| setupKnownHosts(); |
| super.setUp(); |
| } |
| |
| @Override |
| @After |
| public void tearDown() throws Exception { |
| super.tearDown(); |
| |
| if (sshd != null) { |
| try { |
| sshd.stop(true); |
| sshd = null; |
| } catch (Exception e) { |
| // ignore while shutting down as we could be polling during shutdown |
| // and get errors when the ssh server is stopping. |
| } |
| } |
| deleteDirectory(getScpPath()); |
| } |
| |
| protected final String getScpPath() { |
| // use this convention and use separate directories for tests |
| // (easier to debug and avoid interference) |
| return SCP_ROOT_DIR + "/" + getClass().getSimpleName(); |
| } |
| |
| protected String getScpUri() { |
| return "scp://localhost:" + getPort() + "/" + getScpPath(); |
| } |
| |
| protected boolean startSshd() { |
| sshd = SshServer.setUpDefaultServer(); |
| sshd.setPort(getPort()); |
| sshd.setKeyPairProvider(new FileKeyPairProvider(new String[]{"src/test/resources/hostkey.pem"})); |
| sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory())); |
| sshd.setCommandFactory(new ScpCommandFactory()); |
| sshd.setPasswordAuthenticator(new PasswordAuthenticator() { |
| @Override |
| public boolean authenticate(String username, String password, ServerSession session) { |
| // dummy authentication: allow any user whose password is the same as the username |
| return username != null && username.equals(password); |
| } |
| }); |
| sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() { |
| @Override |
| public boolean authenticate(String username, PublicKey key, ServerSession session) { |
| return true; |
| } |
| }); |
| try { |
| sshd.start(); |
| return true; |
| } catch (IOException e) { |
| LOG.info("Failed to start ssh server.", e); |
| } |
| return false; |
| } |
| |
| protected void setupKnownHosts() { |
| knownHostsFile = SCP_ROOT_DIR + "/" + KNOWN_HOSTS; |
| if (!acceptLocalhostConnections) { |
| return; |
| } |
| |
| // For security reasons (avoiding man in the middle attacks), |
| // camel-jsch will only connect to known hosts. For unit testing |
| // we use a known key, but since the port is dynamic, the |
| // known_hosts file will be generated by the following code and |
| // should contain a line like below (if |
| // "HashKnownHosts"=="yes" the hostname:port part will be |
| // hashed and look a bit more complicated). |
| // |
| // [localhost]:21000 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDd \ |
| // fIWeSV4o68dRrKSzFd/Bk51E65UTmmSrmW0O1ohtzi6HzsDPjXgCtlTt3F \ |
| // qTcfFfI92IlTr4JWqC9UK1QT1ZTeng0MkPQmv68hDANHbt5CpETZHjW5q4 \ |
| // OOgWhVvj5IyOC2NZHtKlJBkdsMAa15ouOOJLzBvAvbqOR/yUROsEiQ== |
| |
| JSch jsch = new JSch(); |
| try { |
| LOG.debug("Using '{}' for known hosts.", knownHostsFile); |
| jsch.setKnownHosts(knownHostsFile); |
| Session s = jsch.getSession("admin", "localhost", getPort()); |
| s.setConfig("StrictHostKeyChecking", "ask"); |
| |
| // TODO: by the current jsch (0.1.51) setting "HashKnownHosts" to "no" is a workaround |
| // to make the tests run green, see also http://sourceforge.net/p/jsch/bugs/63/ |
| s.setConfig("HashKnownHosts", "no"); |
| s.setUserInfo(new UserInfo() { |
| @Override |
| public String getPassphrase() { |
| return null; |
| } |
| @Override |
| public String getPassword() { |
| return "admin"; |
| } |
| @Override |
| public boolean promptPassword(String message) { |
| return true; |
| } |
| @Override |
| public boolean promptPassphrase(String message) { |
| return false; |
| } |
| @Override |
| public boolean promptYesNo(String message) { |
| // accept host authenticity |
| return true; |
| } |
| @Override |
| public void showMessage(String message) { |
| } |
| }); |
| // in the process of connecting, "[localhost]:<port>" is added to the knownHostsFile |
| s.connect(); |
| s.disconnect(); |
| } catch (JSchException e) { |
| LOG.info("Could not add [localhost] to known hosts", e); |
| } |
| } |
| |
| public String getKnownHostsFile() { |
| return knownHostsFile; |
| } |
| |
| public boolean isSetupComplete() { |
| return setupComplete; |
| } |
| |
| protected static void traceSecurityProviders() { |
| for (Provider p : Security.getProviders()) { |
| for (Service s : p.getServices()) { |
| LOG.trace("Security provider {} for '{}' algorithm", s.getClassName(), s.getAlgorithm()); |
| } |
| } |
| } |
| } |