summaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.test
diff options
context:
space:
mode:
authorThomas Wolf <thomas.wolf@paranor.ch>2018-09-14 22:40:08 +0200
committerMatthias Sohn <matthias.sohn@sap.com>2018-11-06 13:17:21 +0100
commit08b0a8632d54a24d92075b94d0b0134b69146ba2 (patch)
treec7f85adceb9529545fbfb004a6bbd6559c71dd37 /org.eclipse.jgit.test
parent3ec3ca0f097cb27b190a17f4fde6880c871951e0 (diff)
downloadjgit-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.test')
-rw-r--r--org.eclipse.jgit.test/BUILD4
-rw-r--r--org.eclipse.jgit.test/META-INF/MANIFEST.MF2
-rw-r--r--org.eclipse.jgit.test/pom.xml12
-rw-r--r--org.eclipse.jgit.test/tests.bzl8
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/transport/id_dsa_test15
-rw-r--r--org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/transport/id_dsa_test.pub1
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java113
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java15
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SshTestBase.java466
9 files changed, 635 insertions, 1 deletions
diff --git a/org.eclipse.jgit.test/BUILD b/org.eclipse.jgit.test/BUILD
index 186de25d94..07597f3251 100644
--- a/org.eclipse.jgit.test/BUILD
+++ b/org.eclipse.jgit.test/BUILD
@@ -20,6 +20,7 @@ HELPERS = glob(["src/**/*.java"]) + [PKG + c for c in [
"revwalk/RevWalkTestCase.java",
"transport/ObjectIdMatcher.java",
"transport/SpiTransport.java",
+ "transport/SshTestBase.java",
"treewalk/FileTreeIteratorWithTimeControl.java",
"treewalk/filter/AlwaysCloneTreeFilter.java",
"test/resources/SampleDataRepositoryTestCase.java",
@@ -44,6 +45,9 @@ java_library(
resources = DATA,
deps = [
"//lib:junit",
+ "//lib:jsch",
+ "//lib:sshd-core",
+ "//lib:sshd-sftp",
"//org.eclipse.jgit:jgit",
"//org.eclipse.jgit.junit:junit",
],
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 6df9e39094..6514277ac5 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -10,6 +10,7 @@ Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
com.jcraft.jsch;version="[0.1.54,0.2.0)",
+ org.eclipse.jgit.annotations;version="[5.2.0,5.3.0)",
org.eclipse.jgit.api;version="[5.2.0,5.3.0)",
org.eclipse.jgit.api.errors;version="[5.2.0,5.3.0)",
org.eclipse.jgit.attributes;version="[5.2.0,5.3.0)",
@@ -34,6 +35,7 @@ Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)",
org.eclipse.jgit.internal.storage.reftree;version="[5.2.0,5.3.0)",
org.eclipse.jgit.internal.transport.parser;version="[5.2.0,5.3.0)",
org.eclipse.jgit.junit;version="[5.2.0,5.3.0)",
+ org.eclipse.jgit.junit.ssh;version="[5.2.0,5.3.0)",
org.eclipse.jgit.lfs;version="[5.2.0,5.3.0)",
org.eclipse.jgit.lib;version="[5.2.0,5.3.0)",
org.eclipse.jgit.merge;version="[5.2.0,5.3.0)",
diff --git a/org.eclipse.jgit.test/pom.xml b/org.eclipse.jgit.test/pom.xml
index 6aa34f5b76..3d5df9a68a 100644
--- a/org.eclipse.jgit.test/pom.xml
+++ b/org.eclipse.jgit.test/pom.xml
@@ -112,6 +112,18 @@
<artifactId>org.eclipse.jgit.pgm</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ <version>${apache-sshd-version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-sftp</artifactId>
+ <version>${apache-sshd-version}</version>
+ </dependency>
</dependencies>
<profiles>
diff --git a/org.eclipse.jgit.test/tests.bzl b/org.eclipse.jgit.test/tests.bzl
index bc06e3ef40..b9ad8b2126 100644
--- a/org.eclipse.jgit.test/tests.bzl
+++ b/org.eclipse.jgit.test/tests.bzl
@@ -41,7 +41,13 @@ def tests(tests):
additional_deps = [
"//lib:jsch",
]
-
+ if src.endswith("JSchSshTest.java"):
+ additional_deps = [
+ "//lib:jsch",
+ "//lib:jzlib",
+ "//lib:sshd-core",
+ "//lib:sshd-sftp",
+ ]
heap_size = "-Xmx256m"
if src.endswith("HugeCommitMessageTest.java"):
heap_size = "-Xmx512m"
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/transport/id_dsa_test b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/transport/id_dsa_test
new file mode 100644
index 0000000000..cc39e8b870
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/transport/id_dsa_test
@@ -0,0 +1,15 @@
+-----BEGIN DSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-128-CBC,D7B8FC3F4E304A2A22754B068767081F
+
+IewkLt6JyqtPccAsnfeLv7IMlLvgm7tqQSYK1/CLhmDE0aZXViD8sqxLA6dVjmkp
+BVyk7EBpp43PnVQYsDcMPnyM8H83vNRDtIQ6fxM1PJafiP7Rbn1k1fDh7DwA48PU
+FnT6zZ9aYEKYMto0WIdQ86j/uY+LtYygQDDoZ2ohn2NlpykeyrSp0bDRIoW6sdc5
++LlfDtq2usv3fcxMAJpO/SSN78LvBlyOK4n/JAVSkPawsW1WsIrXA52mk0iUhjYc
+aYOCuL+wA7OmHAOpfS5HUXZ4i/7qONnLBkEqeIOcgTmShh1c4oWw9TjWK1AzdSDU
+G2nkRJ/8zK/jdm5wcmrjrzuREM1VbCiXHlVoHYI0W1Z9etOgz1cj4KLz/bB8Nf+8
+shCez1Aw5ec33BzwysfwymfAKeXjYaxdKcur3j+UdXAlYRD28BRnWmTL5Jx82eUu
+NIh0U9pHkn+PjdzmjSPEUP7wzDjQQacaQTkBRf5gPyOYfv/+Mnq6YyflKaPYmkEr
+eztO22VZlpyp/hj2LzBav9wi0++teInNQGU+GxHedsWm4+YpffMhz1bz5ZUQ670A
+0WJJH3k/KnxbCY3usj4eJr+CsX+LNZhm+rKyjRDmRwA=
+-----END DSA PRIVATE KEY-----
diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/transport/id_dsa_test.pub b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/transport/id_dsa_test.pub
new file mode 100644
index 0000000000..0528a928c9
--- /dev/null
+++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/transport/id_dsa_test.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAIsXi0EUiI6GmhHqrwwjvO2wdujW46+uXM/SG2GVI3KxCSf95B2XgXBsgiKH0sy3guyqjDcP4Ph5Mctg1IxqmqugN6xf9YB6lf09bRdIbumVGU6nXW7bZDHdk9nmvWy56vurofwvhoRnQBUJ3L4n7dxxvXhIyRPOxptayOS2ZcnRAAAAFQDsgGxVxcBBM9y0Rm3kNz/R64CYEQAAAIEAgCbyCJNZb66KQBMO7B+NPxx0caSKjZ+3TpWL6pLJGTAu1pztd1wpElECNCEBhTX9p1HEypTIjOUFU2gjgaBLUcWE0JK+/4vJjjvaENvrQardH0EeRfrazhpRY+X6ytUTk0YPDuQn+ZqBhXxAoD8BA+TJMvk7oMpMUTyr6LGBuj4AAACAeXCfOrKY6wHuMkHHpa9Ix95T+7h5ZrSosrV1WO5g9X04LNiPFRXvGyMWYF17VaGqVWID5NbbGP4PqwSw0rjmw7c/xxV2DYNfJ5NFWsDHxhI6RP9AaGTKcdIEykWEkGgJDiVF/DJgjvapGCW4Lo5UB1JJRXEM4YmTiEbyUyahKqw= thomas@Arcturus
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java
new file mode 100644
index 0000000000..7b0344070e
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/JSchSshTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.transport;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import org.eclipse.jgit.errors.TransportException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.transport.OpenSshConfig.Host;
+import org.eclipse.jgit.util.FS;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+public class JSchSshTest extends SshTestBase {
+
+ private class TestSshSessionFactory extends JschConfigSessionFactory {
+
+ @Override
+ protected void configure(Host hc, Session session) {
+ // Nothing
+ }
+
+ @Override
+ public synchronized RemoteSession getSession(URIish uri,
+ CredentialsProvider credentialsProvider, FS fs, int tms)
+ throws TransportException {
+ return super.getSession(uri, credentialsProvider, fs, tms);
+ }
+
+ @Override
+ protected JSch createDefaultJSch(FS fs) throws JSchException {
+ JSch defaultJSch = super.createDefaultJSch(fs);
+ if (knownHosts.exists()) {
+ defaultJSch.setKnownHosts(knownHosts.getAbsolutePath());
+ }
+ return defaultJSch;
+ }
+ }
+
+ @Override
+ protected SshSessionFactory createSessionFactory() {
+ return new TestSshSessionFactory();
+ }
+
+ @Override
+ protected void installConfig(String... config) {
+ SshSessionFactory factory = getSessionFactory();
+ assertTrue(factory instanceof JschConfigSessionFactory);
+ JschConfigSessionFactory j = (JschConfigSessionFactory) factory;
+ try {
+ j.setConfig(createConfig(config));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private OpenSshConfig createConfig(String... content) throws IOException {
+ File configFile = new File(sshDir, Constants.CONFIG);
+ if (content != null) {
+ Files.write(configFile.toPath(), Arrays.asList(content));
+ }
+ return new OpenSshConfig(getTemporaryDirectory(), configFile);
+ }
+
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
index 19fcbfd7a2..0760585761 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/OpenSshConfigTest.java
@@ -67,6 +67,7 @@ import org.junit.Before;
import org.junit.Test;
import com.jcraft.jsch.ConfigRepository;
+import com.jcraft.jsch.ConfigRepository.Config;
public class OpenSshConfigTest extends RepositoryTestCase {
private File home;
@@ -164,6 +165,20 @@ public class OpenSshConfigTest extends RepositoryTestCase {
}
@Test
+ public void testCaseInsensitiveKeyLookup() throws Exception {
+ config("Host orcz\n" + "Port 29418\n"
+ + "\tHostName repo.or.cz\nStrictHostKeyChecking yes\n");
+ final Host h = osc.lookup("orcz");
+ Config c = h.getConfig();
+ String exactCase = c.getValue("StrictHostKeyChecking");
+ assertEquals("yes", exactCase);
+ assertEquals(exactCase, c.getValue("stricthostkeychecking"));
+ assertEquals(exactCase, c.getValue("STRICTHOSTKEYCHECKING"));
+ assertEquals(exactCase, c.getValue("sTrIcThostKEYcheckING"));
+ assertNull(c.getValue("sTrIcThostKEYcheckIN"));
+ }
+
+ @Test
public void testAlias_DoesNotMatch() throws Exception {
config("Host orcz\n" + "Port 29418\n" + "\tHostName repo.or.cz\n");
final Host h = osc.lookup("repo.or.cz");
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SshTestBase.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SshTestBase.java
new file mode 100644
index 0000000000..0d625f319d
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/SshTestBase.java
@@ -0,0 +1,466 @@
+/*
+ * 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.transport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jgit.api.CloneCommand;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.TransportException;
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.junit.ssh.SshTestGitServer;
+import org.eclipse.jgit.util.FS;
+import org.junit.After;
+import org.junit.Test;
+
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.KeyPair;
+
+public abstract class SshTestBase extends RepositoryTestCase {
+
+ protected static final String TEST_USER = "testuser";
+
+ protected File sshDir;
+
+ protected File privateKey1;
+
+ protected File privateKey2;
+
+ private SshTestGitServer server;
+
+ private SshSessionFactory factory;
+
+ protected int testPort;
+
+ protected File knownHosts;
+
+ private File homeDir;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ writeTrashFile("file.txt", "something");
+ try (Git git = new Git(db)) {
+ git.add().addFilepattern("file.txt").call();
+ git.commit().setMessage("Initial commit").call();
+ }
+ mockSystemReader.setProperty("user.home",
+ getTemporaryDirectory().getAbsolutePath());
+ mockSystemReader.setProperty("HOME",
+ getTemporaryDirectory().getAbsolutePath());
+ homeDir = FS.DETECTED.userHome();
+ FS.DETECTED.setUserHome(getTemporaryDirectory().getAbsoluteFile());
+ sshDir = new File(getTemporaryDirectory(), ".ssh");
+ assertTrue(sshDir.mkdir());
+ File serverDir = new File(getTemporaryDirectory(), "srv");
+ assertTrue(serverDir.mkdir());
+ // Create two key pairs. Let's not call them "id_rsa".
+ privateKey1 = new File(sshDir, "first_key");
+ privateKey2 = new File(sshDir, "second_key");
+ createKeyPair(privateKey1);
+ createKeyPair(privateKey2);
+ ByteArrayOutputStream publicHostKey = new ByteArrayOutputStream();
+ // Start a server with our test user and the first key.
+ server = new SshTestGitServer(TEST_USER, privateKey1.toPath(), db,
+ createHostKey(publicHostKey));
+ testPort = server.start();
+ assertTrue(testPort > 0);
+ knownHosts = new File(sshDir, "known_hosts");
+ Files.write(knownHosts.toPath(), Collections.singleton("[localhost]:"
+ + testPort + ' '
+ + publicHostKey.toString(StandardCharsets.US_ASCII.name())));
+ factory = createSessionFactory();
+ SshSessionFactory.setInstance(factory);
+ }
+
+ private static void createKeyPair(File privateKeyFile) throws Exception {
+ // Found no way to do this with MINA sshd except rolling it all
+ // ourselves...
+ JSch jsch = new JSch();
+ KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048);
+ try (OutputStream out = new FileOutputStream(privateKeyFile)) {
+ pair.writePrivateKey(out);
+ }
+ File publicKeyFile = new File(privateKeyFile.getParentFile(),
+ privateKeyFile.getName() + ".pub");
+ try (OutputStream out = new FileOutputStream(publicKeyFile)) {
+ pair.writePublicKey(out, TEST_USER);
+ }
+ }
+
+ private static byte[] createHostKey(OutputStream publicKey)
+ throws Exception {
+ JSch jsch = new JSch();
+ KeyPair pair = KeyPair.genKeyPair(jsch, KeyPair.RSA, 2048);
+ pair.writePublicKey(publicKey, "");
+ try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ pair.writePrivateKey(out);
+ out.flush();
+ return out.toByteArray();
+ }
+ }
+
+ @After
+ public void shutdownServer() throws Exception {
+ if (server != null) {
+ server.stop();
+ server = null;
+ }
+ FS.DETECTED.setUserHome(homeDir);
+ SshSessionFactory.setInstance(null);
+ factory = null;
+ }
+
+ protected abstract SshSessionFactory createSessionFactory();
+
+ protected SshSessionFactory getSessionFactory() {
+ return factory;
+ }
+
+ protected abstract void installConfig(String... config);
+
+ @Test(expected = TransportException.class)
+ public void testSshCloneWithoutConfig() throws Exception {
+ cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter", null);
+ }
+
+ @Test
+ public void testSshCloneWithGlobalIdentity() throws Exception {
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ null,
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshCloneWithDefaultIdentity() throws Exception {
+ File idRsa = new File(privateKey1.getParentFile(), "id_rsa");
+ Files.copy(privateKey1.toPath(), idRsa.toPath());
+ // We expect the session factory to pick up these keys...
+ cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter", null);
+ }
+
+ @Test
+ public void testSshCloneWithConfig() throws Exception {
+ cloneWith("ssh://localhost/doesntmatter", null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshCloneWithConfigEncryptedUnusedKey() throws Exception {
+ // Copy the encrypted test key from the bundle.
+ File encryptedKey = new File(sshDir, "id_dsa");
+ try (InputStream in = SshTestBase.class
+ .getResourceAsStream("id_dsa_test")) {
+ Files.copy(in, encryptedKey.toPath());
+ }
+ TestCredentialsProvider provider = new TestCredentialsProvider(
+ "testpass");
+ cloneWith("ssh://localhost/doesntmatter", provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ assertEquals("CredentialsProvider should not have been called", 0,
+ provider.getLog().size());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshCloneWithoutKnownHosts() throws Exception {
+ assertTrue("Could not delete known_hosts", knownHosts.delete());
+ cloneWith("ssh://localhost/doesntmatter", null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshCloneWithoutKnownHostsWithProvider() throws Exception {
+ File copiedHosts = new File(knownHosts.getParentFile(),
+ "copiedKnownHosts");
+ assertTrue("Failed to rename known_hosts",
+ knownHosts.renameTo(copiedHosts));
+ TestCredentialsProvider provider = new TestCredentialsProvider();
+ cloneWith("ssh://localhost/doesntmatter", provider, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ Map<URIish, List<CredentialItem>> messages = provider.getLog();
+ assertFalse("Expected user iteraction", messages.isEmpty());
+ }
+
+ @Test
+ public void testSftpCloneWithConfig() throws Exception {
+ cloneWith("sftp://localhost/.git", null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test(expected = TransportException.class)
+ public void testSshCloneWithConfigWrongKey() throws Exception {
+ cloneWith("ssh://localhost/doesntmatter", null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey2.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshCloneWithWrongUserNameInConfig() throws Exception {
+ // Bug 526778
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "User sombody_else", //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshCloneWithWrongPortInConfig() throws Exception {
+ // Bug 526778
+ cloneWith(
+ "ssh://" + TEST_USER + "@localhost:" + testPort
+ + "/doesntmatter",
+ null, //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port 22", //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshCloneWithAliasInConfig() throws Exception {
+ // Bug 531118
+ cloneWith("ssh://git/doesntmatter", null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), "", //
+ "Host localhost", //
+ "HostName localhost", //
+ "Port 22", //
+ "User someone_else", //
+ "IdentityFile " + privateKey2.getAbsolutePath());
+ }
+
+ @Test
+ public void testSshCloneWithUnknownCiphersInConfig() throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr");
+ }
+
+ @Test
+ public void testSshCloneWithUnknownHostKeyAlgorithmsInConfig()
+ throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "HostKeyAlgorithms foobar,ssh-rsa,ssh-dss");
+ }
+
+ @Test
+ public void testSshCloneWithUnknownKexAlgorithmsInConfig()
+ throws Exception {
+ // Bug 535672
+ cloneWith("ssh://git/doesntmatter", null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "KexAlgorithms foobar,diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521");
+ }
+
+ @Test
+ public void testSshCloneWithMinimalHostKeyAlgorithmsInConfig()
+ throws Exception {
+ // Bug 537790
+ cloneWith("ssh://git/doesntmatter", null, //
+ "Host git", //
+ "HostName localhost", //
+ "Port " + testPort, //
+ "User " + TEST_USER, //
+ "IdentityFile " + privateKey1.getAbsolutePath(), //
+ "HostKeyAlgorithms ssh-rsa,ssh-dss");
+ }
+
+ private void cloneWith(String uri, CredentialsProvider provider,
+ String... config) throws Exception {
+ installConfig(config);
+ File cloned = new File(getTemporaryDirectory(), "cloned");
+ CloneCommand clone = Git.cloneRepository().setCloneAllBranches(true)
+ .setDirectory(cloned).setURI(uri);
+ if (provider != null) {
+ clone.setCredentialsProvider(provider);
+ }
+ try (Git git = clone.call()) {
+ assertNotNull(git.getRepository().resolve("master"));
+ assertNotEquals(db.getWorkTree(),
+ git.getRepository().getWorkTree());
+ checkFile(new File(git.getRepository().getWorkTree(), "file.txt"),
+ "something");
+ }
+ }
+
+ private class TestCredentialsProvider extends CredentialsProvider {
+
+ private final List<String> stringStore;
+
+ private final Iterator<String> strings;
+
+ public TestCredentialsProvider(String... strings) {
+ if (strings == null || strings.length == 0) {
+ stringStore = Collections.emptyList();
+ } else {
+ stringStore = Arrays.asList(strings);
+ }
+ this.strings = stringStore.iterator();
+ }
+
+ @Override
+ public boolean isInteractive() {
+ return true;
+ }
+
+ @Override
+ public boolean supports(CredentialItem... items) {
+ return true;
+ }
+
+ @Override
+ public boolean get(URIish uri, CredentialItem... items)
+ throws UnsupportedCredentialItem {
+ System.out.println("URI: " + uri);
+ for (CredentialItem item : items) {
+ System.out.println(item.getClass().getSimpleName() + ' '
+ + item.getPromptText());
+ }
+ logItems(uri, items);
+ for (CredentialItem item : items) {
+ if (item instanceof CredentialItem.InformationalMessage) {
+ continue;
+ }
+ if (item instanceof CredentialItem.YesNoType) {
+ ((CredentialItem.YesNoType) item).setValue(true);
+ } else if (item instanceof CredentialItem.CharArrayType) {
+ if (strings.hasNext()) {
+ ((CredentialItem.CharArrayType) item)
+ .setValue(strings.next().toCharArray());
+ } else {
+ return false;
+ }
+ } else if (item instanceof CredentialItem.StringType) {
+ if (strings.hasNext()) {
+ ((CredentialItem.StringType) item)
+ .setValue(strings.next());
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private Map<URIish, List<CredentialItem>> log = new LinkedHashMap<>();
+
+ private void logItems(URIish uri, CredentialItem... items) {
+ log.put(uri, Arrays.asList(items));
+ }
+
+ public Map<URIish, List<CredentialItem>> getLog() {
+ return log;
+ }
+ }
+}