diff options
Diffstat (limited to 'org.eclipse.jgit.test/src/org')
-rw-r--r-- | org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java | 811 | ||||
-rw-r--r-- | org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java | 419 |
2 files changed, 0 insertions, 1230 deletions
diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java deleted file mode 100644 index c22c10cb7f..0000000000 --- a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestBase.java +++ /dev/null @@ -1,811 +0,0 @@ -/* - * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Distribution License v. 1.0 which is available at - * https://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: BSD-3-Clause - */ -package org.eclipse.jgit.transport.ssh; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.List; -import java.util.Locale; - -import org.eclipse.jgit.api.errors.TransportException; -import org.eclipse.jgit.transport.CredentialItem; -import org.eclipse.jgit.transport.JschConfigSessionFactory; -import org.junit.Test; -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theory; - -/** - * The ssh tests. Concrete subclasses can re-use these tests by implementing the - * abstract operations from {@link SshTestHarness}. This gives a way to test - * different ssh clients against a unified test suite. - */ -public abstract class SshTestBase extends SshTestHarness { - - @DataPoints - public static String[] KEY_RESOURCES = { // - "id_dsa", // - "id_rsa_1024", // - "id_rsa_2048", // - "id_rsa_3072", // - "id_rsa_4096", // - "id_ecdsa_256", // - "id_ecdsa_384", // - "id_ecdsa_521", // - "id_ed25519", // - // And now encrypted. Passphrase is "testpass". - "id_dsa_testpass", // - "id_rsa_1024_testpass", // - "id_rsa_2048_testpass", // - "id_rsa_3072_testpass", // - "id_rsa_4096_testpass", // - "id_ecdsa_256_testpass", // - "id_ecdsa_384_testpass", // - "id_ecdsa_521_testpass", // - "id_ed25519_testpass", // - "id_ed25519_expensive_testpass" }; - - protected File defaultCloneDir; - - @Override - public void setUp() throws Exception { - super.setUp(); - defaultCloneDir = new File(getTemporaryDirectory(), "cloned"); - } - - @Test(expected = TransportException.class) - public void testSshWithoutConfig() throws Exception { - cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort - + "/doesntmatter", defaultCloneDir, null); - } - - @Test - public void testSshWithGlobalIdentity() throws Exception { - cloneWith( - "ssh://" + TEST_USER + "@localhost:" + testPort - + "/doesntmatter", - defaultCloneDir, null, - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testSshWithDefaultIdentity() 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", defaultCloneDir, null); - } - - @Test - public void testSshWithConfig() throws Exception { - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testSshWithConfigEncryptedUnusedKey() throws Exception { - // Copy the encrypted test key from the bundle. - File encryptedKey = new File(sshDir, "id_dsa"); - copyTestResource("id_dsa_testpass", encryptedKey); - TestCredentialsProvider provider = new TestCredentialsProvider( - "testpass"); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, 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 - public void testSshWithConfigEncryptedUnusedKeyInConfigLast() - throws Exception { - // Copy the encrypted test key from the bundle. - File encryptedKey = new File(sshDir, "id_dsa_test_key"); - copyTestResource("id_dsa_testpass", encryptedKey); - TestCredentialsProvider provider = new TestCredentialsProvider( - "testpass"); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath(), - "IdentityFile " + encryptedKey.getAbsolutePath()); - // This test passes with JSch per chance because JSch completely ignores - // the second IdentityFile - assertEquals("CredentialsProvider should not have been called", 0, - provider.getLog().size()); - } - - @Test - public void testSshWithConfigEncryptedUnusedKeyInConfigFirst() - throws Exception { - // Test cannot pass with JSch; it handles only one IdentityFile. - // assumeTrue(!(getSessionFactory() instanceof - // JschConfigSessionFactory)); gives in bazel a failure with "Never - // found parameters that satisfied method assumptions." - // In maven it's fine!? - if (getSessionFactory() instanceof JschConfigSessionFactory) { - return; - } - // Copy the encrypted test key from the bundle. - File encryptedKey = new File(sshDir, "id_dsa_test_key"); - copyTestResource("id_dsa_testpass", encryptedKey); - TestCredentialsProvider provider = new TestCredentialsProvider( - "testpass"); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + encryptedKey.getAbsolutePath(), - "IdentityFile " + privateKey1.getAbsolutePath()); - assertEquals("CredentialsProvider should have been called once", 1, - provider.getLog().size()); - } - - @Test - public void testSshEncryptedUsedKeyCached() throws Exception { - // Make sure we are asked for the password only once if we do several - // operations with an encrypted key. - File encryptedKey = new File(sshDir, "id_dsa_test_key"); - copyTestResource("id_dsa_testpass", encryptedKey); - File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub"); - copyTestResource("id_dsa_testpass.pub", encryptedPublicKey); - server.setTestUserPublicKey(encryptedPublicKey.toPath()); - TestCredentialsProvider provider = new TestCredentialsProvider( - "testpass"); - pushTo(provider, - cloneWith("ssh://localhost/doesntmatter", // - defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + encryptedKey.getAbsolutePath())); - assertEquals("CredentialsProvider should have been called once", 1, - provider.getLog().size()); - } - - @Test(expected = TransportException.class) - public void testSshEncryptedUsedKeyWrongPassword() throws Exception { - File encryptedKey = new File(sshDir, "id_dsa_test_key"); - copyTestResource("id_dsa_testpass", encryptedKey); - File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub"); - copyTestResource("id_dsa_testpass.pub", encryptedPublicKey); - server.setTestUserPublicKey(encryptedPublicKey.toPath()); - TestCredentialsProvider provider = new TestCredentialsProvider( - "wrongpass"); - cloneWith("ssh://localhost/doesntmatter", // - defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "NumberOfPasswordPrompts 1", // - "IdentityFile " + encryptedKey.getAbsolutePath()); - } - - @Test - public void testSshEncryptedUsedKeySeveralPassword() throws Exception { - File encryptedKey = new File(sshDir, "id_dsa_test_key"); - copyTestResource("id_dsa_testpass", encryptedKey); - File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub"); - copyTestResource("id_dsa_testpass.pub", encryptedPublicKey); - server.setTestUserPublicKey(encryptedPublicKey.toPath()); - TestCredentialsProvider provider = new TestCredentialsProvider( - "wrongpass", "wrongpass2", "testpass"); - cloneWith("ssh://localhost/doesntmatter", // - defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + encryptedKey.getAbsolutePath()); - assertEquals("CredentialsProvider should have been called 3 times", 3, - provider.getLog().size()); - } - - @Test(expected = TransportException.class) - public void testSshWithoutKnownHosts() throws Exception { - assertTrue("Could not delete known_hosts", knownHosts.delete()); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testSshWithoutKnownHostsWithProviderAsk() - throws Exception { - File copiedHosts = new File(knownHosts.getParentFile(), - "copiedKnownHosts"); - assertTrue("Failed to rename known_hosts", - knownHosts.renameTo(copiedHosts)); - // The provider will answer "yes" to all questions, so we should be able - // to connect and end up with a new known_hosts file with the host key. - TestCredentialsProvider provider = new TestCredentialsProvider(); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - List<LogEntry> messages = provider.getLog(); - assertFalse("Expected user interaction", messages.isEmpty()); - if (getSessionFactory() instanceof JschConfigSessionFactory) { - // JSch doesn't create a non-existing file. - assertEquals("Expected to be asked about the key", 1, - messages.size()); - return; - } - assertEquals( - "Expected to be asked about the key, and the file creation", - 2, messages.size()); - assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists()); - // Instead of checking the file contents, let's just clone again - // without provider. If it works, the server host key was written - // correctly. - File clonedAgain = new File(getTemporaryDirectory(), "cloned2"); - cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testSshWithoutKnownHostsWithProviderAcceptNew() - 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", defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "StrictHostKeyChecking accept-new", // - "IdentityFile " + privateKey1.getAbsolutePath()); - if (getSessionFactory() instanceof JschConfigSessionFactory) { - // JSch doesn't create new files. - assertTrue("CredentialsProvider not called", - provider.getLog().isEmpty()); - return; - } - assertEquals("Expected to be asked about the file creation", 1, - provider.getLog().size()); - assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists()); - // Instead of checking the file contents, let's just clone again - // without provider. If it works, the server host key was written - // correctly. - File clonedAgain = new File(getTemporaryDirectory(), "cloned2"); - cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test(expected = TransportException.class) - public void testSshWithoutKnownHostsDeny() throws Exception { - File copiedHosts = new File(knownHosts.getParentFile(), - "copiedKnownHosts"); - assertTrue("Failed to rename known_hosts", - knownHosts.renameTo(copiedHosts)); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "StrictHostKeyChecking yes", // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test(expected = TransportException.class) - public void testSshModifiedHostKeyDeny() - throws Exception { - File copiedHosts = new File(knownHosts.getParentFile(), - "copiedKnownHosts"); - assertTrue("Failed to rename known_hosts", - knownHosts.renameTo(copiedHosts)); - // Now produce a new known_hosts file containing some other key. - createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "StrictHostKeyChecking yes", // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test(expected = TransportException.class) - public void testSshModifiedHostKeyWithProviderDeny() throws Exception { - File copiedHosts = new File(knownHosts.getParentFile(), - "copiedKnownHosts"); - assertTrue("Failed to rename known_hosts", - knownHosts.renameTo(copiedHosts)); - // Now produce a new known_hosts file containing some other key. - createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1); - TestCredentialsProvider provider = new TestCredentialsProvider(); - try { - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "StrictHostKeyChecking yes", // - "IdentityFile " + privateKey1.getAbsolutePath()); - } catch (Exception e) { - assertEquals("Expected to be told about the modified key", 1, - provider.getLog().size()); - assertTrue("Only messages expected", provider.getLog().stream() - .flatMap(l -> l.getItems().stream()).allMatch( - c -> c instanceof CredentialItem.InformationalMessage)); - throw e; - } - } - - private void checkKnownHostsModifiedHostKey(File backup, File newFile, - String wrongKey) throws IOException { - List<String> oldLines = Files.readAllLines(backup.toPath(), UTF_8); - // Find the original entry. We should have that again in known_hosts. - String oldKeyPart = null; - for (String oldLine : oldLines) { - if (oldLine.contains("[localhost]:")) { - String[] parts = oldLine.split("\\s+"); - if (parts.length > 2) { - oldKeyPart = parts[parts.length - 2] + ' ' - + parts[parts.length - 1]; - break; - } - } - } - assertNotNull("Old key not found", oldKeyPart); - List<String> newLines = Files.readAllLines(newFile.toPath(), UTF_8); - assertFalse("Old host key still found in known_hosts file" + newFile, - hasHostKey("localhost", testPort, wrongKey, newLines)); - assertTrue("New host key not found in known_hosts file" + newFile, - hasHostKey("localhost", testPort, oldKeyPart, newLines)); - - } - - @Test - public void testSshModifiedHostKeyAllow() throws Exception { - assertTrue("Failed to delete known_hosts", knownHosts.delete()); - createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1); - File backup = new File(getTemporaryDirectory(), "backupKnownHosts"); - Files.copy(knownHosts.toPath(), backup.toPath()); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "StrictHostKeyChecking no", // - "IdentityFile " + privateKey1.getAbsolutePath()); - // File should not have been updated! - String[] oldLines = Files - .readAllLines(backup.toPath(), UTF_8) - .toArray(new String[0]); - String[] newLines = Files - .readAllLines(knownHosts.toPath(), UTF_8) - .toArray(new String[0]); - assertArrayEquals("Known hosts file should not be modified", oldLines, - newLines); - } - - @Test - public void testSshModifiedHostKeyAsk() throws Exception { - File copiedHosts = new File(knownHosts.getParentFile(), - "copiedKnownHosts"); - assertTrue("Failed to rename known_hosts", - knownHosts.renameTo(copiedHosts)); - String wrongKeyPart = createKnownHostsFile(knownHosts, "localhost", - testPort, publicKey1); - TestCredentialsProvider provider = new TestCredentialsProvider(); - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - checkKnownHostsModifiedHostKey(copiedHosts, knownHosts, wrongKeyPart); - assertEquals("Expected to be asked about the modified key", 1, - provider.getLog().size()); - } - - @Test - public void testSshCloneWithConfigAndPush() throws Exception { - pushTo(cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath())); - } - - @Test - public void testSftpWithConfig() throws Exception { - cloneWith("sftp://localhost/.git", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testSftpCloneWithConfigAndPush() throws Exception { - pushTo(cloneWith("sftp://localhost/.git", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath())); - } - - @Test(expected = TransportException.class) - public void testSshWithConfigWrongKey() throws Exception { - cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey2.getAbsolutePath()); - } - - @Test - public void testSshWithWrongUserNameInConfig() throws Exception { - // Bug 526778 - cloneWith( - "ssh://" + TEST_USER + "@localhost:" + testPort - + "/doesntmatter", - defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "User sombody_else", // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testSshWithWrongPortInConfig() throws Exception { - // Bug 526778 - cloneWith( - "ssh://" + TEST_USER + "@localhost:" + testPort - + "/doesntmatter", - defaultCloneDir, null, // - "Host localhost", // - "HostName localhost", // - "Port 22", // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testSshWithAliasInConfig() throws Exception { - // Bug 531118 - cloneWith("ssh://git/doesntmatter", defaultCloneDir, 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 testSshWithUnknownCiphersInConfig() throws Exception { - // Bug 535672 - cloneWith("ssh://git/doesntmatter", defaultCloneDir, 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 testSshWithUnknownHostKeyAlgorithmsInConfig() - throws Exception { - // Bug 535672 - cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath(), // - "HostKeyAlgorithms foobar,ssh-rsa,ssh-dss"); - } - - @Test - public void testSshWithUnknownKexAlgorithmsInConfig() - throws Exception { - // Bug 535672 - cloneWith("ssh://git/doesntmatter", defaultCloneDir, 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 testSshWithMinimalHostKeyAlgorithmsInConfig() - throws Exception { - // Bug 537790 - cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath(), // - "HostKeyAlgorithms ssh-rsa,ssh-dss"); - } - - @Test - public void testSshWithUnknownAuthInConfig() throws Exception { - cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath(), // - "PreferredAuthentications gssapi-with-mic,hostbased,publickey,keyboard-interactive,password"); - } - - @Test(expected = TransportException.class) - public void testSshWithNoMatchingAuthInConfig() throws Exception { - // Server doesn't do password, and anyway we set no password. - cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath(), // - "PreferredAuthentications password"); - } - - @Test - public void testRsaHostKeySecond() throws Exception { - // See https://git.eclipse.org/r/#/c/130402/ : server has EcDSA - // (preferred), RSA, we have RSA in known_hosts: client and server - // should agree on RSA. - File newHostKey = new File(getTemporaryDirectory(), "newhostkey"); - copyTestResource("id_ecdsa_256", newHostKey); - server.addHostKey(newHostKey.toPath(), true); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testEcDsaHostKey() throws Exception { - // See https://git.eclipse.org/r/#/c/130402/ : server has RSA - // (preferred), EcDSA, we have EcDSA in known_hosts: client and server - // should agree on EcDSA. - File newHostKey = new File(getTemporaryDirectory(), "newhostkey"); - copyTestResource("id_ecdsa_256", newHostKey); - server.addHostKey(newHostKey.toPath(), false); - File newHostKeyPub = new File(getTemporaryDirectory(), - "newhostkey.pub"); - copyTestResource("id_ecdsa_256.pub", newHostKeyPub); - createKnownHostsFile(knownHosts, "localhost", testPort, newHostKeyPub); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey1.getAbsolutePath()); - } - - @Test - public void testPasswordAuth() throws Exception { - server.enablePasswordAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider( - TEST_USER.toUpperCase(Locale.ROOT)); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications password"); - } - - @Test - public void testPasswordAuthSeveralTimes() throws Exception { - server.enablePasswordAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider( - "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT)); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications password"); - } - - @Test(expected = TransportException.class) - public void testPasswordAuthWrongPassword() throws Exception { - server.enablePasswordAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider( - "wrongpass"); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications password"); - } - - @Test(expected = TransportException.class) - public void testPasswordAuthNoPassword() throws Exception { - server.enablePasswordAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider(); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications password"); - } - - @Test(expected = TransportException.class) - public void testPasswordAuthCorrectPasswordTooLate() throws Exception { - server.enablePasswordAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider( - "wrongpass", "wrongpass", "wrongpass", - TEST_USER.toUpperCase(Locale.ROOT)); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications password"); - } - - @Test - public void testKeyboardInteractiveAuth() throws Exception { - server.enableKeyboardInteractiveAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider( - TEST_USER.toUpperCase(Locale.ROOT)); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications keyboard-interactive"); - } - - @Test - public void testKeyboardInteractiveAuthSeveralTimes() throws Exception { - server.enableKeyboardInteractiveAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider( - "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT)); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications keyboard-interactive"); - } - - @Test(expected = TransportException.class) - public void testKeyboardInteractiveAuthWrongPassword() throws Exception { - server.enableKeyboardInteractiveAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider( - "wrongpass"); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications keyboard-interactive"); - } - - @Test(expected = TransportException.class) - public void testKeyboardInteractiveAuthNoPassword() throws Exception { - server.enableKeyboardInteractiveAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider(); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications keyboard-interactive"); - } - - @Test(expected = TransportException.class) - public void testKeyboardInteractiveAuthCorrectPasswordTooLate() - throws Exception { - server.enableKeyboardInteractiveAuthentication(); - TestCredentialsProvider provider = new TestCredentialsProvider( - "wrongpass", "wrongpass", "wrongpass", - TEST_USER.toUpperCase(Locale.ROOT)); - cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, // - "Host git", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "PreferredAuthentications keyboard-interactive"); - } - - @Theory - public void testSshKeys(String keyName) throws Exception { - // JSch fails on ECDSA 384/521 keys. Compare - // https://sourceforge.net/p/jsch/patches/10/ - assumeTrue(!(getSessionFactory() instanceof JschConfigSessionFactory - && (keyName.contains("ed25519") - || keyName.startsWith("id_ecdsa_384") - || keyName.startsWith("id_ecdsa_521")))); - File cloned = new File(getTemporaryDirectory(), "cloned"); - String keyFileName = keyName + "_key"; - File privateKey = new File(sshDir, keyFileName); - copyTestResource(keyName, privateKey); - File publicKey = new File(sshDir, keyFileName + ".pub"); - copyTestResource(keyName + ".pub", publicKey); - server.setTestUserPublicKey(publicKey.toPath()); - TestCredentialsProvider provider = new TestCredentialsProvider( - "testpass"); - pushTo(provider, - cloneWith("ssh://localhost/doesntmatter", // - cloned, provider, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey.getAbsolutePath())); - int expectedCalls = keyName.endsWith("testpass") ? 1 : 0; - assertEquals("Unexpected calls to CredentialsProvider", expectedCalls, - provider.getLog().size()); - // Should now also work without credentials provider, even if the key - // was encrypted. - cloned = new File(getTemporaryDirectory(), "cloned2"); - pushTo(null, - cloneWith("ssh://localhost/doesntmatter", // - cloned, null, // - "Host localhost", // - "HostName localhost", // - "Port " + testPort, // - "User " + TEST_USER, // - "IdentityFile " + privateKey.getAbsolutePath())); - } -} diff --git a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java b/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java deleted file mode 100644 index 632c24b890..0000000000 --- a/org.eclipse.jgit.test/src/org/eclipse/jgit/transport/ssh/SshTestHarness.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch> and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Distribution License v. 1.0 which is available at - * https://www.eclipse.org/org/documents/edl-v10.php. - * - * SPDX-License-Identifier: BSD-3-Clause - */ -package org.eclipse.jgit.transport.ssh; - -import static java.nio.charset.StandardCharsets.US_ASCII; -import static java.nio.charset.StandardCharsets.UTF_8; -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.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import org.eclipse.jgit.api.CloneCommand; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.PushCommand; -import org.eclipse.jgit.api.ResetCommand.ResetType; -import org.eclipse.jgit.errors.UnsupportedCredentialItem; -import org.eclipse.jgit.junit.RepositoryTestCase; -import org.eclipse.jgit.junit.ssh.SshTestGitServer; -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.transport.CredentialItem; -import org.eclipse.jgit.transport.CredentialsProvider; -import org.eclipse.jgit.transport.PushResult; -import org.eclipse.jgit.transport.RemoteRefUpdate; -import org.eclipse.jgit.transport.SshSessionFactory; -import org.eclipse.jgit.transport.URIish; -import org.eclipse.jgit.util.FS; -import org.junit.After; - -import com.jcraft.jsch.JSch; -import com.jcraft.jsch.KeyPair; - -/** - * Root class for ssh tests. Sets up the ssh test server. A set of pre-computed - * keys for testing is provided in the bundle and can be used in test cases via - * {@link #copyTestResource(String, File)}. These test key files names have four - * components, separated by a single underscore: "id", the algorithm, the bits - * (if variable), and the password if the private key is encrypted. For instance - * "{@code id_ecdsa_384_testpass}" is an encrypted ECDSA-384 key. The passphrase - * to decrypt is "testpass". The key "{@code id_ecdsa_384}" is the same but - * unencrypted. All keys were generated and encrypted via ssh-keygen. Note that - * DSA and ec25519 have no "bits" component. Available keys are listed in - * {@link SshTestBase#KEY_RESOURCES}. - */ -public abstract class SshTestHarness extends RepositoryTestCase { - - protected static final String TEST_USER = "testuser"; - - protected File sshDir; - - protected File privateKey1; - - protected File privateKey2; - - protected File publicKey1; - - protected 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"); - publicKey1 = createKeyPair(privateKey1); - createKeyPair(privateKey2); - ByteArrayOutputStream publicHostKey = new ByteArrayOutputStream(); - // Start a server with our test user and the first key. - server = new SshTestGitServer(TEST_USER, publicKey1.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(US_ASCII.name()))); - factory = createSessionFactory(); - SshSessionFactory.setInstance(factory); - } - - private static File 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); - } - return publicKeyFile; - } - - 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(); - } - } - - /** - * Creates a new known_hosts file with one entry for the given host and port - * taken from the given public key file. - * - * @param file - * to write the known_hosts file to - * @param host - * for the entry - * @param port - * for the entry - * @param publicKey - * to use - * @return the public-key part of the line - * @throws IOException - */ - protected static String createKnownHostsFile(File file, String host, - int port, File publicKey) throws IOException { - List<String> lines = Files.readAllLines(publicKey.toPath(), UTF_8); - assertEquals("Public key has too many lines", 1, lines.size()); - String pubKey = lines.get(0); - // Strip off the comment. - String[] parts = pubKey.split("\\s+"); - assertTrue("Unexpected key content", - parts.length == 2 || parts.length == 3); - String keyPart = parts[0] + ' ' + parts[1]; - String line = '[' + host + "]:" + port + ' ' + keyPart; - Files.write(file.toPath(), Collections.singletonList(line)); - return keyPart; - } - - /** - * Checks whether there is a line for the given host and port that also - * matches the given key part in the list of lines. - * - * @param host - * to look for - * @param port - * to look for - * @param keyPart - * to look for - * @param lines - * to look in - * @return {@code true} if found, {@code false} otherwise - */ - protected boolean hasHostKey(String host, int port, String keyPart, - List<String> lines) { - String h = '[' + host + "]:" + port; - return lines.stream() - .anyMatch(l -> l.contains(h) && l.contains(keyPart)); - } - - @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); - - /** - * Copies a test data file contained in the test bundle to the given file. - * Equivalent to {@link #copyTestResource(Class, String, File)} with - * {@code SshTestHarness.class} as first parameter. - * - * @param resourceName - * of the test resource to copy - * @param to - * file to copy the resource to - * @throws IOException - * if the resource cannot be copied - */ - protected void copyTestResource(String resourceName, File to) - throws IOException { - copyTestResource(SshTestHarness.class, resourceName, to); - } - - /** - * Copies a test data file contained in the test bundle to the given file, - * using {@link Class#getResourceAsStream(String)} to get the test resource. - * - * @param loader - * {@link Class} to use to load the resource - * @param resourceName - * of the test resource to copy - * @param to - * file to copy the resource to - * @throws IOException - * if the resource cannot be copied - */ - protected void copyTestResource(Class<?> loader, String resourceName, - File to) throws IOException { - try (InputStream in = loader.getResourceAsStream(resourceName)) { - Files.copy(in, to.toPath()); - } - } - - protected File cloneWith(String uri, File to, CredentialsProvider provider, - String... config) throws Exception { - installConfig(config); - CloneCommand clone = Git.cloneRepository().setCloneAllBranches(true) - .setDirectory(to).setURI(uri); - if (provider != null) { - clone.setCredentialsProvider(provider); - } - try (Git git = clone.call()) { - Repository repo = git.getRepository(); - assertNotNull(repo.resolve("master")); - assertNotEquals(db.getWorkTree(), - git.getRepository().getWorkTree()); - assertTrue(new File(git.getRepository().getWorkTree(), "file.txt") - .exists()); - return repo.getWorkTree(); - } - } - - protected void pushTo(File localClone) throws Exception { - pushTo(null, localClone); - } - - protected void pushTo(CredentialsProvider provider, File localClone) - throws Exception { - RevCommit commit; - File newFile = null; - try (Git git = Git.open(localClone)) { - // Write a new file and modify a file. - Repository local = git.getRepository(); - newFile = File.createTempFile("new", "sshtest", - local.getWorkTree()); - write(newFile, "something new"); - File existingFile = new File(local.getWorkTree(), "file.txt"); - write(existingFile, "something else"); - git.add().addFilepattern("file.txt") - .addFilepattern(newFile.getName()) - .call(); - commit = git.commit().setMessage("Local commit").call(); - // Push - PushCommand push = git.push().setPushAll(); - if (provider != null) { - push.setCredentialsProvider(provider); - } - Iterable<PushResult> results = push.call(); - for (PushResult result : results) { - for (RemoteRefUpdate u : result.getRemoteUpdates()) { - assertEquals( - "Could not update " + u.getRemoteName() + ' ' - + u.getMessage(), - RemoteRefUpdate.Status.OK, u.getStatus()); - } - } - } - // Now check "master" in the remote repo directly: - assertEquals("Unexpected remote commit", commit, db.resolve("master")); - assertEquals("Unexpected remote commit", commit, - db.resolve(Constants.HEAD)); - File remoteFile = new File(db.getWorkTree(), newFile.getName()); - assertFalse("File should not exist on remote", remoteFile.exists()); - try (Git git = new Git(db)) { - git.reset().setMode(ResetType.HARD).setRef(Constants.HEAD).call(); - } - assertTrue("File does not exist on remote", remoteFile.exists()); - checkFile(remoteFile, "something new"); - } - - protected static 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 List<LogEntry> log = new ArrayList<>(); - - private void logItems(URIish uri, CredentialItem... items) { - log.add(new LogEntry(uri, Arrays.asList(items))); - } - - public List<LogEntry> getLog() { - return log; - } - } - - protected static class LogEntry { - - private URIish uri; - - private List<CredentialItem> items; - - public LogEntry(URIish uri, List<CredentialItem> items) { - this.uri = uri; - this.items = items; - } - - public URIish getURIish() { - return uri; - } - - public List<CredentialItem> getItems() { - return items; - } - } -} |