diff options
author | Thomas Wolf <thomas.wolf@paranor.ch> | 2018-09-14 22:40:08 +0200 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2018-11-06 13:17:21 +0100 |
commit | 08b0a8632d54a24d92075b94d0b0134b69146ba2 (patch) | |
tree | c7f85adceb9529545fbfb004a6bbd6559c71dd37 /org.eclipse.jgit.junit/src | |
parent | 3ec3ca0f097cb27b190a17f4fde6880c871951e0 (diff) | |
download | jgit-08b0a8632d54a24d92075b94d0b0134b69146ba2.tar.gz jgit-08b0a8632d54a24d92075b94d0b0134b69146ba2.zip |
Ssh tests with an Apache MINA sshd test git server
Add a simple ssh git server based on Apache MINA sshd, and use it
in new tests that verify ssh operations and in particular a number
of bugs that had cropped up over time in JSch.
The git server supports fetching only, and sftp access.
The tests are all in an abstract base class; the concrete JschSshTest
class only provides ssh-specific test setup. So the same tests could
be run easily also with some other ssh client.
Bug: 520927
Change-Id: Ide6687b717fb497a29fc83f22b07390a26dfce1d
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
Diffstat (limited to 'org.eclipse.jgit.junit/src')
-rw-r--r-- | org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java new file mode 100644 index 0000000000..675a11589d --- /dev/null +++ b/org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/ssh/SshTestGitServer.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.eclipse.jgit.junit.ssh; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.PublicKey; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.apache.sshd.common.config.keys.IdentityUtils; +import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory; +import org.apache.sshd.common.keyprovider.KeyPairProvider; +import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.server.SshServer; +import org.apache.sshd.server.command.AbstractCommandSupport; +import org.apache.sshd.server.shell.UnknownCommand; +import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; +import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.UploadPack; + +/** + * A simple ssh/sftp git <em>test</em> server based on Apache MINA sshd. + * <p> + * Supports only a single repository. Authenticates only the given test user + * against his given test public key. ssh is limited to fetching (upload-pack). + * </p> + * + * @since 5.2 + */ +public class SshTestGitServer { + + @NonNull + private String testUser; + + @NonNull + private PublicKey testKey; + + @NonNull + private Repository repository; + + private final ExecutorService executorService = Executors + .newFixedThreadPool(2); + + private final SshServer server; + + /** + * Creates a ssh git <em>test</em> server. It serves one single repository, + * and accepts public-key authentication for exactly one test user. + * + * @param testUser + * user name of the test user + * @param testKey + * <em>private</em> key file of the test user; the server will + * only user the public key from it + * @param repository + * to serve + * @param hostKey + * the unencrypted private key to use as host key + * @throws IOException + * @throws GeneralSecurityException + */ + public SshTestGitServer(@NonNull String testUser, @NonNull Path testKey, + @NonNull Repository repository, @NonNull byte[] hostKey) + throws IOException, GeneralSecurityException { + this.testUser = testUser; + this.testKey = IdentityUtils + .loadIdentities(Collections.singletonMap("A", testKey), null) + .get("A").getPublic(); + this.repository = repository; + server = SshServer.setUpDefaultServer(); + // Set host key + server.setKeyPairProvider(new KeyPairProvider() { + + @Override + public Iterable<KeyPair> loadKeys() { + try (ByteArrayInputStream in = new ByteArrayInputStream( + hostKey)) { + return Collections.singletonList( + SecurityUtils.loadKeyPairIdentity("", in, null)); + } catch (IOException | GeneralSecurityException e) { + return null; + } + } + + }); + // SFTP. + server.setFileSystemFactory(new VirtualFileSystemFactory() { + + @Override + protected Path computeRootDir(Session session) throws IOException { + return SshTestGitServer.this.repository.getDirectory() + .getParentFile().getAbsoluteFile().toPath(); + } + }); + server.setSubsystemFactories(Collections + .singletonList((new SftpSubsystemFactory.Builder()).build())); + // No shell + server.setShellFactory(null); + // Disable some authentications + server.setPasswordAuthenticator(null); + server.setKeyboardInteractiveAuthenticator(null); + server.setGSSAuthenticator(null); + server.setHostBasedAuthenticator(null); + // Accept only the test user/public key + server.setPublickeyAuthenticator((userName, publicKey, session) -> { + return SshTestGitServer.this.testUser.equals(userName) && KeyUtils + .compareKeys(SshTestGitServer.this.testKey, publicKey); + }); + server.setCommandFactory(command -> { + if (command.startsWith("git-upload-pack") + || command.startsWith("git upload-pack")) { + return new GitUploadPackCommand(command, executorService); + } + return new UnknownCommand(command); + }); + } + + /** + * Starts the test server, listening on a random port. + * + * @return the port the server listens on; test clients should connect to + * that port + * @throws IOException + */ + public int start() throws IOException { + server.start(); + return server.getPort(); + } + + /** + * Stops the test server. + * + * @throws IOException + */ + public void stop() throws IOException { + executorService.shutdownNow(); + server.stop(true); + } + + private class GitUploadPackCommand extends AbstractCommandSupport { + + protected GitUploadPackCommand(String command, + ExecutorService executorService) { + super(command, executorService, false); + } + + @Override + public void run() { + UploadPack uploadPack = new UploadPack(repository); + String gitProtocol = getEnvironment().getEnv().get("GIT_PROTOCOL"); + if (gitProtocol != null) { + uploadPack + .setExtraParameters(Collections.singleton(gitProtocol)); + } + try { + uploadPack.upload(getInputStream(), getOutputStream(), + getErrorStream()); + onExit(0); + } catch (IOException e) { + log.warn( + MessageFormat.format("Could not run {0}", getCommand()), + e); + onExit(-1, e.toString()); + } + } + + } +} |