1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.junit.ssh;
11
12 import static java.nio.charset.StandardCharsets.UTF_8;
13 import static org.junit.Assert.assertArrayEquals;
14 import static org.junit.Assert.assertEquals;
15 import static org.junit.Assert.assertFalse;
16 import static org.junit.Assert.assertNotNull;
17 import static org.junit.Assert.assertThrows;
18 import static org.junit.Assert.assertTrue;
19 import static org.junit.Assume.assumeTrue;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.file.Files;
24 import java.util.List;
25 import java.util.Locale;
26 import java.util.concurrent.TimeUnit;
27
28 import org.eclipse.jgit.api.errors.TransportException;
29 import org.eclipse.jgit.errors.CommandFailedException;
30 import org.eclipse.jgit.transport.CredentialItem;
31 import org.eclipse.jgit.transport.URIish;
32 import org.eclipse.jgit.util.FS;
33 import org.eclipse.jgit.util.SshSupport;
34 import org.junit.Test;
35 import org.junit.experimental.theories.DataPoints;
36 import org.junit.experimental.theories.Theory;
37
38
39
40
41
42
43 public abstract class SshTestBase extends SshTestHarness {
44
45 @DataPoints
46 public static String[] KEY_RESOURCES = {
47 "id_dsa",
48 "id_rsa_1024",
49 "id_rsa_2048",
50 "id_rsa_3072",
51 "id_rsa_4096",
52 "id_ecdsa_256",
53 "id_ecdsa_384",
54 "id_ecdsa_521",
55 "id_ed25519",
56
57 "id_dsa_testpass",
58 "id_rsa_1024_testpass",
59 "id_rsa_2048_testpass",
60 "id_rsa_3072_testpass",
61 "id_rsa_4096_testpass",
62 "id_ecdsa_256_testpass",
63 "id_ecdsa_384_testpass",
64 "id_ecdsa_521_testpass",
65 "id_ed25519_testpass",
66 "id_ed25519_expensive_testpass" };
67
68 protected File defaultCloneDir;
69
70 @Override
71 public void setUp() throws Exception {
72 super.setUp();
73 defaultCloneDir = new File(getTemporaryDirectory(), "cloned");
74 }
75
76 @Test
77 public void testSshWithoutConfig() throws Exception {
78 assertThrows(TransportException.class,
79 () -> cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
80 + "/doesntmatter", defaultCloneDir, null));
81 }
82
83 @Test
84 public void testSingleCommand() throws Exception {
85 installConfig("IdentityFile " + privateKey1.getAbsolutePath());
86 String command = SshTestGitServer.ECHO_COMMAND + " 1 without timeout";
87 long start = System.nanoTime();
88 String reply = SshSupport.runSshCommand(
89 new URIish("ssh://" + TEST_USER + "@localhost:" + testPort),
90 null, FS.DETECTED, command, 0);
91 long elapsed = System.nanoTime() - start;
92 assertEquals(command, reply);
93
94
95 command = SshTestGitServer.ECHO_COMMAND + " 1 expecting no timeout";
96
97 int timeout = 10 * ((int) TimeUnit.NANOSECONDS.toSeconds(elapsed) + 1);
98 reply = SshSupport.runSshCommand(
99 new URIish("ssh://" + TEST_USER + "@localhost:" + testPort),
100 null, FS.DETECTED, command, timeout);
101 assertEquals(command, reply);
102 }
103
104 @Test
105 public void testSingleCommandWithTimeoutExpired() throws Exception {
106 installConfig("IdentityFile " + privateKey1.getAbsolutePath());
107 String command = SshTestGitServer.ECHO_COMMAND + " 2 EXPECTING TIMEOUT";
108
109 CommandFailedException e = assertThrows(CommandFailedException.class,
110 () -> SshSupport.runSshCommand(new URIish(
111 "ssh://" + TEST_USER + "@localhost:" + testPort), null,
112 FS.DETECTED, command, 1));
113 assertTrue(e.getMessage().contains(command));
114 assertTrue(e.getMessage().contains("time"));
115 }
116
117 @Test
118 public void testSshWithGlobalIdentity() throws Exception {
119 cloneWith(
120 "ssh://" + TEST_USER + "@localhost:" + testPort
121 + "/doesntmatter",
122 defaultCloneDir, null,
123 "IdentityFile " + privateKey1.getAbsolutePath());
124 }
125
126 @Test
127 public void testSshWithDefaultIdentity() throws Exception {
128 File idRsa = new File(privateKey1.getParentFile(), "id_rsa");
129 Files.copy(privateKey1.toPath(), idRsa.toPath());
130
131 cloneWith("ssh://" + TEST_USER + "@localhost:" + testPort
132 + "/doesntmatter", defaultCloneDir, null);
133 }
134
135 @Test
136 public void testSshWithConfig() throws Exception {
137 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
138 "Host localhost",
139 "HostName localhost",
140 "Port " + testPort,
141 "User " + TEST_USER,
142 "IdentityFile " + privateKey1.getAbsolutePath());
143 }
144
145 @Test
146 public void testSshWithConfigEncryptedUnusedKey() throws Exception {
147
148 File encryptedKey = new File(sshDir, "id_dsa");
149 copyTestResource("id_dsa_testpass", encryptedKey);
150 TestCredentialsProvider provider = new TestCredentialsProvider(
151 "testpass");
152 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
153 "Host localhost",
154 "HostName localhost",
155 "Port " + testPort,
156 "User " + TEST_USER,
157 "IdentityFile " + privateKey1.getAbsolutePath());
158 assertEquals("CredentialsProvider should not have been called", 0,
159 provider.getLog().size());
160 }
161
162 @Test
163 public void testSshWithConfigEncryptedUnusedKeyInConfigLast()
164 throws Exception {
165
166 File encryptedKey = new File(sshDir, "id_dsa_test_key");
167 copyTestResource("id_dsa_testpass", encryptedKey);
168 TestCredentialsProvider provider = new TestCredentialsProvider(
169 "testpass");
170 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
171 "Host localhost",
172 "HostName localhost",
173 "Port " + testPort,
174 "User " + TEST_USER,
175 "IdentityFile " + privateKey1.getAbsolutePath(),
176 "IdentityFile " + encryptedKey.getAbsolutePath());
177
178
179 assertEquals("CredentialsProvider should not have been called", 0,
180 provider.getLog().size());
181 }
182
183 private boolean isJsch() {
184 return getSessionFactory().getType().equals("jsch");
185 }
186
187 @Test
188 public void testSshWithConfigEncryptedUnusedKeyInConfigFirst()
189 throws Exception {
190
191
192
193
194
195 if (isJsch()) {
196 return;
197 }
198
199 File encryptedKey = new File(sshDir, "id_dsa_test_key");
200 copyTestResource("id_dsa_testpass", encryptedKey);
201 TestCredentialsProvider provider = new TestCredentialsProvider(
202 "testpass");
203 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
204 "Host localhost",
205 "HostName localhost",
206 "Port " + testPort,
207 "User " + TEST_USER,
208 "IdentityFile " + encryptedKey.getAbsolutePath(),
209 "IdentityFile " + privateKey1.getAbsolutePath());
210 assertEquals("CredentialsProvider should have been called once", 1,
211 provider.getLog().size());
212 }
213
214 @Test
215 public void testSshEncryptedUsedKeyCached() throws Exception {
216
217
218 File encryptedKey = new File(sshDir, "id_dsa_test_key");
219 copyTestResource("id_dsa_testpass", encryptedKey);
220 File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
221 copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
222 server.setTestUserPublicKey(encryptedPublicKey.toPath());
223 TestCredentialsProvider provider = new TestCredentialsProvider(
224 "testpass");
225 pushTo(provider,
226 cloneWith("ssh://localhost/doesntmatter", //
227 defaultCloneDir, provider,
228 "Host localhost",
229 "HostName localhost",
230 "Port " + testPort,
231 "User " + TEST_USER,
232 "IdentityFile " + encryptedKey.getAbsolutePath()));
233 assertEquals("CredentialsProvider should have been called once", 1,
234 provider.getLog().size());
235 }
236
237 @Test(expected = TransportException.class)
238 public void testSshEncryptedUsedKeyWrongPassword() throws Exception {
239 File encryptedKey = new File(sshDir, "id_dsa_test_key");
240 copyTestResource("id_dsa_testpass", encryptedKey);
241 File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
242 copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
243 server.setTestUserPublicKey(encryptedPublicKey.toPath());
244 TestCredentialsProvider provider = new TestCredentialsProvider(
245 "wrongpass");
246 cloneWith("ssh://localhost/doesntmatter", //
247 defaultCloneDir, provider,
248 "Host localhost",
249 "HostName localhost",
250 "Port " + testPort,
251 "User " + TEST_USER,
252 "NumberOfPasswordPrompts 1",
253 "IdentityFile " + encryptedKey.getAbsolutePath());
254 }
255
256 @Test
257 public void testSshEncryptedUsedKeySeveralPassword() throws Exception {
258 File encryptedKey = new File(sshDir, "id_dsa_test_key");
259 copyTestResource("id_dsa_testpass", encryptedKey);
260 File encryptedPublicKey = new File(sshDir, "id_dsa_test_key.pub");
261 copyTestResource("id_dsa_testpass.pub", encryptedPublicKey);
262 server.setTestUserPublicKey(encryptedPublicKey.toPath());
263 TestCredentialsProvider provider = new TestCredentialsProvider(
264 "wrongpass", "wrongpass2", "testpass");
265 cloneWith("ssh://localhost/doesntmatter", //
266 defaultCloneDir, provider,
267 "Host localhost",
268 "HostName localhost",
269 "Port " + testPort,
270 "User " + TEST_USER,
271 "IdentityFile " + encryptedKey.getAbsolutePath());
272 assertEquals("CredentialsProvider should have been called 3 times", 3,
273 provider.getLog().size());
274 }
275
276 @Test(expected = TransportException.class)
277 public void testSshWithoutKnownHosts() throws Exception {
278 assertTrue("Could not delete known_hosts", knownHosts.delete());
279 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
280 "Host localhost",
281 "HostName localhost",
282 "Port " + testPort,
283 "User " + TEST_USER,
284 "IdentityFile " + privateKey1.getAbsolutePath());
285 }
286
287 @Test
288 public void testSshWithoutKnownHostsWithProviderAsk()
289 throws Exception {
290 File copiedHosts = new File(knownHosts.getParentFile(),
291 "copiedKnownHosts");
292 assertTrue("Failed to rename known_hosts",
293 knownHosts.renameTo(copiedHosts));
294
295
296 TestCredentialsProvider provider = new TestCredentialsProvider();
297 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
298 "Host localhost",
299 "HostName localhost",
300 "Port " + testPort,
301 "User " + TEST_USER,
302 "IdentityFile " + privateKey1.getAbsolutePath());
303 List<LogEntry> messages = provider.getLog();
304 assertFalse("Expected user interaction", messages.isEmpty());
305 if (isJsch()) {
306
307 assertEquals("Expected to be asked about the key", 1,
308 messages.size());
309 return;
310 }
311 assertEquals(
312 "Expected to be asked about the key, and the file creation",
313 2, messages.size());
314 assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists());
315
316
317
318 File clonedAgain = new File(getTemporaryDirectory(), "cloned2");
319 cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, //
320 "Host localhost",
321 "HostName localhost",
322 "Port " + testPort,
323 "User " + TEST_USER,
324 "IdentityFile " + privateKey1.getAbsolutePath());
325 }
326
327 @Test
328 public void testSshWithoutKnownHostsWithProviderAcceptNew()
329 throws Exception {
330 File copiedHosts = new File(knownHosts.getParentFile(),
331 "copiedKnownHosts");
332 assertTrue("Failed to rename known_hosts",
333 knownHosts.renameTo(copiedHosts));
334 TestCredentialsProvider provider = new TestCredentialsProvider();
335 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
336 "Host localhost",
337 "HostName localhost",
338 "Port " + testPort,
339 "User " + TEST_USER,
340 "StrictHostKeyChecking accept-new",
341 "IdentityFile " + privateKey1.getAbsolutePath());
342 if (isJsch()) {
343
344 assertTrue("CredentialsProvider not called",
345 provider.getLog().isEmpty());
346 return;
347 }
348 assertEquals("Expected to be asked about the file creation", 1,
349 provider.getLog().size());
350 assertTrue("~/.ssh/known_hosts should exist now", knownHosts.exists());
351
352
353
354 File clonedAgain = new File(getTemporaryDirectory(), "cloned2");
355 cloneWith("ssh://localhost/doesntmatter", clonedAgain, null, //
356 "Host localhost",
357 "HostName localhost",
358 "Port " + testPort,
359 "User " + TEST_USER,
360 "IdentityFile " + privateKey1.getAbsolutePath());
361 }
362
363 @Test(expected = TransportException.class)
364 public void testSshWithoutKnownHostsDeny() throws Exception {
365 File copiedHosts = new File(knownHosts.getParentFile(),
366 "copiedKnownHosts");
367 assertTrue("Failed to rename known_hosts",
368 knownHosts.renameTo(copiedHosts));
369 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
370 "Host localhost",
371 "HostName localhost",
372 "Port " + testPort,
373 "User " + TEST_USER,
374 "StrictHostKeyChecking yes",
375 "IdentityFile " + privateKey1.getAbsolutePath());
376 }
377
378 @Test(expected = TransportException.class)
379 public void testSshModifiedHostKeyDeny()
380 throws Exception {
381 File copiedHosts = new File(knownHosts.getParentFile(),
382 "copiedKnownHosts");
383 assertTrue("Failed to rename known_hosts",
384 knownHosts.renameTo(copiedHosts));
385
386 createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
387 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
388 "Host localhost",
389 "HostName localhost",
390 "Port " + testPort,
391 "User " + TEST_USER,
392 "StrictHostKeyChecking yes",
393 "IdentityFile " + privateKey1.getAbsolutePath());
394 }
395
396 @Test(expected = TransportException.class)
397 public void testSshModifiedHostKeyWithProviderDeny() throws Exception {
398 File copiedHosts = new File(knownHosts.getParentFile(),
399 "copiedKnownHosts");
400 assertTrue("Failed to rename known_hosts",
401 knownHosts.renameTo(copiedHosts));
402
403 createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
404 TestCredentialsProvider provider = new TestCredentialsProvider();
405 try {
406 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
407 "Host localhost",
408 "HostName localhost",
409 "Port " + testPort,
410 "User " + TEST_USER,
411 "StrictHostKeyChecking yes",
412 "IdentityFile " + privateKey1.getAbsolutePath());
413 } catch (Exception e) {
414 assertEquals("Expected to be told about the modified key", 1,
415 provider.getLog().size());
416 assertTrue("Only messages expected", provider.getLog().stream()
417 .flatMap(l -> l.getItems().stream()).allMatch(
418 c -> c instanceof CredentialItem.InformationalMessage));
419 throw e;
420 }
421 }
422
423 private void checkKnownHostsModifiedHostKey(File backup, File newFile,
424 String wrongKey) throws IOException {
425 List<String> oldLines = Files.readAllLines(backup.toPath(), UTF_8);
426
427 String oldKeyPart = null;
428 for (String oldLine : oldLines) {
429 if (oldLine.contains("[localhost]:")) {
430 String[] parts = oldLine.split("\\s+");
431 if (parts.length > 2) {
432 oldKeyPart = parts[parts.length - 2] + ' '
433 + parts[parts.length - 1];
434 break;
435 }
436 }
437 }
438 assertNotNull("Old key not found", oldKeyPart);
439 List<String> newLines = Files.readAllLines(newFile.toPath(), UTF_8);
440 assertFalse("Old host key still found in known_hosts file" + newFile,
441 hasHostKey("localhost", testPort, wrongKey, newLines));
442 assertTrue("New host key not found in known_hosts file" + newFile,
443 hasHostKey("localhost", testPort, oldKeyPart, newLines));
444
445 }
446
447 @Test
448 public void testSshModifiedHostKeyAllow() throws Exception {
449 assertTrue("Failed to delete known_hosts", knownHosts.delete());
450 createKnownHostsFile(knownHosts, "localhost", testPort, publicKey1);
451 File backup = new File(getTemporaryDirectory(), "backupKnownHosts");
452 Files.copy(knownHosts.toPath(), backup.toPath());
453 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
454 "Host localhost",
455 "HostName localhost",
456 "Port " + testPort,
457 "User " + TEST_USER,
458 "StrictHostKeyChecking no",
459 "IdentityFile " + privateKey1.getAbsolutePath());
460
461 String[] oldLines = Files
462 .readAllLines(backup.toPath(), UTF_8)
463 .toArray(new String[0]);
464 String[] newLines = Files
465 .readAllLines(knownHosts.toPath(), UTF_8)
466 .toArray(new String[0]);
467 assertArrayEquals("Known hosts file should not be modified", oldLines,
468 newLines);
469 }
470
471 @Test
472 public void testSshModifiedHostKeyAsk() throws Exception {
473 File copiedHosts = new File(knownHosts.getParentFile(),
474 "copiedKnownHosts");
475 assertTrue("Failed to rename known_hosts",
476 knownHosts.renameTo(copiedHosts));
477 String wrongKeyPart = createKnownHostsFile(knownHosts, "localhost",
478 testPort, publicKey1);
479 TestCredentialsProvider provider = new TestCredentialsProvider();
480 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, provider, //
481 "Host localhost",
482 "HostName localhost",
483 "Port " + testPort,
484 "User " + TEST_USER,
485 "IdentityFile " + privateKey1.getAbsolutePath());
486 checkKnownHostsModifiedHostKey(copiedHosts, knownHosts, wrongKeyPart);
487 assertEquals("Expected to be asked about the modified key", 1,
488 provider.getLog().size());
489 }
490
491 @Test
492 public void testSshCloneWithConfigAndPush() throws Exception {
493 pushTo(cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
494 "Host localhost",
495 "HostName localhost",
496 "Port " + testPort,
497 "User " + TEST_USER,
498 "IdentityFile " + privateKey1.getAbsolutePath()));
499 }
500
501 @Test
502 public void testSftpWithConfig() throws Exception {
503 cloneWith("sftp://localhost/.git", defaultCloneDir, null, //
504 "Host localhost",
505 "HostName localhost",
506 "Port " + testPort,
507 "User " + TEST_USER,
508 "IdentityFile " + privateKey1.getAbsolutePath());
509 }
510
511 @Test
512 public void testSftpCloneWithConfigAndPush() throws Exception {
513 pushTo(cloneWith("sftp://localhost/.git", defaultCloneDir, null, //
514 "Host localhost",
515 "HostName localhost",
516 "Port " + testPort,
517 "User " + TEST_USER,
518 "IdentityFile " + privateKey1.getAbsolutePath()));
519 }
520
521 @Test(expected = TransportException.class)
522 public void testSshWithConfigWrongKey() throws Exception {
523 cloneWith("ssh://localhost/doesntmatter", defaultCloneDir, null, //
524 "Host localhost",
525 "HostName localhost",
526 "Port " + testPort,
527 "User " + TEST_USER,
528 "IdentityFile " + privateKey2.getAbsolutePath());
529 }
530
531 @Test
532 public void testSshWithWrongUserNameInConfig() throws Exception {
533
534 cloneWith(
535 "ssh://" + TEST_USER + "@localhost:" + testPort
536 + "/doesntmatter",
537 defaultCloneDir, null,
538 "Host localhost",
539 "HostName localhost",
540 "User sombody_else",
541 "IdentityFile " + privateKey1.getAbsolutePath());
542 }
543
544 @Test
545 public void testSshWithWrongPortInConfig() throws Exception {
546
547 cloneWith(
548 "ssh://" + TEST_USER + "@localhost:" + testPort
549 + "/doesntmatter",
550 defaultCloneDir, null,
551 "Host localhost",
552 "HostName localhost",
553 "Port 22",
554 "User " + TEST_USER,
555 "IdentityFile " + privateKey1.getAbsolutePath());
556 }
557
558 @Test
559 public void testSshWithAliasInConfig() throws Exception {
560
561 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
562 "Host git",
563 "HostName localhost",
564 "Port " + testPort,
565 "User " + TEST_USER,
566 "IdentityFile " + privateKey1.getAbsolutePath(), "",
567 "Host localhost",
568 "HostName localhost",
569 "Port 22",
570 "User someone_else",
571 "IdentityFile " + privateKey2.getAbsolutePath());
572 }
573
574 @Test
575 public void testSshWithUnknownCiphersInConfig() throws Exception {
576
577 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
578 "Host git",
579 "HostName localhost",
580 "Port " + testPort,
581 "User " + TEST_USER,
582 "IdentityFile " + privateKey1.getAbsolutePath(),
583 "Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr");
584 }
585
586 @Test
587 public void testSshWithUnknownHostKeyAlgorithmsInConfig()
588 throws Exception {
589
590 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
591 "Host git",
592 "HostName localhost",
593 "Port " + testPort,
594 "User " + TEST_USER,
595 "IdentityFile " + privateKey1.getAbsolutePath(),
596 "HostKeyAlgorithms foobar,ssh-rsa,ssh-dss");
597 }
598
599 @Test
600 public void testSshWithUnknownKexAlgorithmsInConfig()
601 throws Exception {
602
603 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
604 "Host git",
605 "HostName localhost",
606 "Port " + testPort,
607 "User " + TEST_USER,
608 "IdentityFile " + privateKey1.getAbsolutePath(),
609 "KexAlgorithms foobar,diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521");
610 }
611
612 @Test
613 public void testSshWithMinimalHostKeyAlgorithmsInConfig()
614 throws Exception {
615
616 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
617 "Host git",
618 "HostName localhost",
619 "Port " + testPort,
620 "User " + TEST_USER,
621 "IdentityFile " + privateKey1.getAbsolutePath(),
622 "HostKeyAlgorithms ssh-rsa,ssh-dss");
623 }
624
625 @Test
626 public void testSshWithUnknownAuthInConfig() throws Exception {
627 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
628 "Host git",
629 "HostName localhost",
630 "Port " + testPort,
631 "User " + TEST_USER,
632 "IdentityFile " + privateKey1.getAbsolutePath(),
633 "PreferredAuthentications gssapi-with-mic,hostbased,publickey,keyboard-interactive,password");
634 }
635
636 @Test(expected = TransportException.class)
637 public void testSshWithNoMatchingAuthInConfig() throws Exception {
638
639 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
640 "Host git",
641 "HostName localhost",
642 "Port " + testPort,
643 "User " + TEST_USER,
644 "IdentityFile " + privateKey1.getAbsolutePath(),
645 "PreferredAuthentications password");
646 }
647
648 @Test
649 public void testRsaHostKeySecond() throws Exception {
650
651
652
653 File newHostKey = new File(getTemporaryDirectory(), "newhostkey");
654 copyTestResource("id_ecdsa_256", newHostKey);
655 server.addHostKey(newHostKey.toPath(), true);
656 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
657 "Host git",
658 "HostName localhost",
659 "Port " + testPort,
660 "User " + TEST_USER,
661 "IdentityFile " + privateKey1.getAbsolutePath());
662 }
663
664 @Test
665 public void testEcDsaHostKey() throws Exception {
666
667
668
669 File newHostKey = new File(getTemporaryDirectory(), "newhostkey");
670 copyTestResource("id_ecdsa_256", newHostKey);
671 server.addHostKey(newHostKey.toPath(), false);
672 File newHostKeyPub = new File(getTemporaryDirectory(),
673 "newhostkey.pub");
674 copyTestResource("id_ecdsa_256.pub", newHostKeyPub);
675 createKnownHostsFile(knownHosts, "localhost", testPort, newHostKeyPub);
676 cloneWith("ssh://git/doesntmatter", defaultCloneDir, null, //
677 "Host git",
678 "HostName localhost",
679 "Port " + testPort,
680 "User " + TEST_USER,
681 "IdentityFile " + privateKey1.getAbsolutePath());
682 }
683
684 @Test
685 public void testPasswordAuth() throws Exception {
686 server.enablePasswordAuthentication();
687 TestCredentialsProvider provider = new TestCredentialsProvider(
688 TEST_USER.toUpperCase(Locale.ROOT));
689 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
690 "Host git",
691 "HostName localhost",
692 "Port " + testPort,
693 "User " + TEST_USER,
694 "PreferredAuthentications password");
695 }
696
697 @Test
698 public void testPasswordAuthSeveralTimes() throws Exception {
699 server.enablePasswordAuthentication();
700 TestCredentialsProvider provider = new TestCredentialsProvider(
701 "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT));
702 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
703 "Host git",
704 "HostName localhost",
705 "Port " + testPort,
706 "User " + TEST_USER,
707 "PreferredAuthentications password");
708 }
709
710 @Test(expected = TransportException.class)
711 public void testPasswordAuthWrongPassword() throws Exception {
712 server.enablePasswordAuthentication();
713 TestCredentialsProvider provider = new TestCredentialsProvider(
714 "wrongpass");
715 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
716 "Host git",
717 "HostName localhost",
718 "Port " + testPort,
719 "User " + TEST_USER,
720 "PreferredAuthentications password");
721 }
722
723 @Test(expected = TransportException.class)
724 public void testPasswordAuthNoPassword() throws Exception {
725 server.enablePasswordAuthentication();
726 TestCredentialsProvider provider = new TestCredentialsProvider();
727 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
728 "Host git",
729 "HostName localhost",
730 "Port " + testPort,
731 "User " + TEST_USER,
732 "PreferredAuthentications password");
733 }
734
735 @Test(expected = TransportException.class)
736 public void testPasswordAuthCorrectPasswordTooLate() throws Exception {
737 server.enablePasswordAuthentication();
738 TestCredentialsProvider provider = new TestCredentialsProvider(
739 "wrongpass", "wrongpass", "wrongpass",
740 TEST_USER.toUpperCase(Locale.ROOT));
741 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
742 "Host git",
743 "HostName localhost",
744 "Port " + testPort,
745 "User " + TEST_USER,
746 "PreferredAuthentications password");
747 }
748
749 @Test
750 public void testKeyboardInteractiveAuth() throws Exception {
751 server.enableKeyboardInteractiveAuthentication();
752 TestCredentialsProvider provider = new TestCredentialsProvider(
753 TEST_USER.toUpperCase(Locale.ROOT));
754 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
755 "Host git",
756 "HostName localhost",
757 "Port " + testPort,
758 "User " + TEST_USER,
759 "PreferredAuthentications keyboard-interactive");
760 }
761
762 @Test
763 public void testKeyboardInteractiveAuthSeveralTimes() throws Exception {
764 server.enableKeyboardInteractiveAuthentication();
765 TestCredentialsProvider provider = new TestCredentialsProvider(
766 "wrongpass", "wrongpass", TEST_USER.toUpperCase(Locale.ROOT));
767 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
768 "Host git",
769 "HostName localhost",
770 "Port " + testPort,
771 "User " + TEST_USER,
772 "PreferredAuthentications keyboard-interactive");
773 }
774
775 @Test(expected = TransportException.class)
776 public void testKeyboardInteractiveAuthWrongPassword() throws Exception {
777 server.enableKeyboardInteractiveAuthentication();
778 TestCredentialsProvider provider = new TestCredentialsProvider(
779 "wrongpass");
780 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
781 "Host git",
782 "HostName localhost",
783 "Port " + testPort,
784 "User " + TEST_USER,
785 "PreferredAuthentications keyboard-interactive");
786 }
787
788 @Test(expected = TransportException.class)
789 public void testKeyboardInteractiveAuthNoPassword() throws Exception {
790 server.enableKeyboardInteractiveAuthentication();
791 TestCredentialsProvider provider = new TestCredentialsProvider();
792 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
793 "Host git",
794 "HostName localhost",
795 "Port " + testPort,
796 "User " + TEST_USER,
797 "PreferredAuthentications keyboard-interactive");
798 }
799
800 @Test(expected = TransportException.class)
801 public void testKeyboardInteractiveAuthCorrectPasswordTooLate()
802 throws Exception {
803 server.enableKeyboardInteractiveAuthentication();
804 TestCredentialsProvider provider = new TestCredentialsProvider(
805 "wrongpass", "wrongpass", "wrongpass",
806 TEST_USER.toUpperCase(Locale.ROOT));
807 cloneWith("ssh://git/doesntmatter", defaultCloneDir, provider, //
808 "Host git",
809 "HostName localhost",
810 "Port " + testPort,
811 "User " + TEST_USER,
812 "PreferredAuthentications keyboard-interactive");
813 }
814
815 @Theory
816 public void testSshKeys(String keyName) throws Exception {
817
818
819 assumeTrue(!(isJsch() && (keyName.contains("ed25519")
820 || keyName.startsWith("id_ecdsa_384")
821 || keyName.startsWith("id_ecdsa_521"))));
822 File cloned = new File(getTemporaryDirectory(), "cloned");
823 String keyFileName = keyName + "_key";
824 File privateKey = new File(sshDir, keyFileName);
825 copyTestResource(keyName, privateKey);
826 File publicKey = new File(sshDir, keyFileName + ".pub");
827 copyTestResource(keyName + ".pub", publicKey);
828 server.setTestUserPublicKey(publicKey.toPath());
829 TestCredentialsProvider provider = new TestCredentialsProvider(
830 "testpass");
831 pushTo(provider,
832 cloneWith("ssh://localhost/doesntmatter", //
833 cloned, provider,
834 "Host localhost",
835 "HostName localhost",
836 "Port " + testPort,
837 "User " + TEST_USER,
838 "IdentityFile " + privateKey.getAbsolutePath()));
839 int expectedCalls = keyName.endsWith("testpass") ? 1 : 0;
840 assertEquals("Unexpected calls to CredentialsProvider", expectedCalls,
841 provider.getLog().size());
842
843
844 cloned = new File(getTemporaryDirectory(), "cloned2");
845 pushTo(null,
846 cloneWith("ssh://localhost/doesntmatter", //
847 cloned, null,
848 "Host localhost",
849 "HostName localhost",
850 "Port " + testPort,
851 "User " + TEST_USER,
852 "IdentityFile " + privateKey.getAbsolutePath()));
853 }
854 }