1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.transport;
45
46 import static java.nio.charset.StandardCharsets.UTF_8;
47 import static org.junit.Assert.assertArrayEquals;
48 import static org.junit.Assert.assertEquals;
49 import static org.junit.Assert.assertFalse;
50 import static org.junit.Assert.assertNotNull;
51 import static org.junit.Assert.assertNotSame;
52 import static org.junit.Assert.assertNull;
53 import static org.junit.Assert.assertSame;
54 import static org.junit.Assert.assertTrue;
55
56 import java.io.File;
57 import java.io.FileOutputStream;
58 import java.io.IOException;
59 import java.io.OutputStreamWriter;
60
61 import org.eclipse.jgit.junit.RepositoryTestCase;
62 import org.eclipse.jgit.lib.Constants;
63 import org.eclipse.jgit.transport.OpenSshConfig.Host;
64 import org.eclipse.jgit.util.FileUtils;
65 import org.eclipse.jgit.util.SystemReader;
66 import org.junit.Before;
67 import org.junit.Test;
68
69 import com.jcraft.jsch.ConfigRepository;
70
71 public class OpenSshConfigTest extends RepositoryTestCase {
72 private File home;
73
74 private File configFile;
75
76 private OpenSshConfig osc;
77
78 @Override
79 @Before
80 public void setUp() throws Exception {
81 super.setUp();
82
83 home = new File(trash, "home");
84 FileUtils.mkdir(home);
85
86 configFile = new File(new File(home, ".ssh"), Constants.CONFIG);
87 FileUtils.mkdir(configFile.getParentFile());
88
89 mockSystemReader.setProperty(Constants.OS_USER_NAME_KEY, "jex_junit");
90 osc = new OpenSshConfig(home, configFile);
91 }
92
93 private void config(String data) throws IOException {
94 long lastMtime = configFile.lastModified();
95 do {
96 try (final OutputStreamWriter fw = new OutputStreamWriter(
97 new FileOutputStream(configFile), UTF_8)) {
98 fw.write(data);
99 }
100 } while (lastMtime == configFile.lastModified());
101 }
102
103 @Test
104 public void testNoConfig() {
105 final Host h = osc.lookup("repo.or.cz");
106 assertNotNull(h);
107 assertEquals("repo.or.cz", h.getHostName());
108 assertEquals("jex_junit", h.getUser());
109 assertEquals(22, h.getPort());
110 assertEquals(1, h.getConnectionAttempts());
111 assertNull(h.getIdentityFile());
112 }
113
114 @Test
115 public void testSeparatorParsing() throws Exception {
116 config("Host\tfirst\n" +
117 "\tHostName\tfirst.tld\n" +
118 "\n" +
119 "Host second\n" +
120 " HostName\tsecond.tld\n" +
121 "Host=third\n" +
122 "HostName=third.tld\n\n\n" +
123 "\t Host = fourth\n\n\n" +
124 " \t HostName\t=fourth.tld\n" +
125 "Host\t = last\n" +
126 "HostName \t last.tld");
127 assertNotNull(osc.lookup("first"));
128 assertEquals("first.tld", osc.lookup("first").getHostName());
129 assertNotNull(osc.lookup("second"));
130 assertEquals("second.tld", osc.lookup("second").getHostName());
131 assertNotNull(osc.lookup("third"));
132 assertEquals("third.tld", osc.lookup("third").getHostName());
133 assertNotNull(osc.lookup("fourth"));
134 assertEquals("fourth.tld", osc.lookup("fourth").getHostName());
135 assertNotNull(osc.lookup("last"));
136 assertEquals("last.tld", osc.lookup("last").getHostName());
137 }
138
139 @Test
140 public void testQuoteParsing() throws Exception {
141 config("Host \"good\"\n" +
142 " HostName=\"good.tld\"\n" +
143 " Port=\"6007\"\n" +
144 " User=\"gooduser\"\n" +
145 "Host multiple unquoted and \"quoted\" \"hosts\"\n" +
146 " Port=\"2222\"\n" +
147 "Host \"spaced\"\n" +
148 "# Bad host name, but testing preservation of spaces\n" +
149 " HostName=\" spaced\ttld \"\n" +
150 "# Misbalanced quotes\n" +
151 "Host \"bad\"\n" +
152 "# OpenSSH doesn't allow this but ...\n" +
153 " HostName=bad.tld\"\n");
154 assertEquals("good.tld", osc.lookup("good").getHostName());
155 assertEquals("gooduser", osc.lookup("good").getUser());
156 assertEquals(6007, osc.lookup("good").getPort());
157 assertEquals(2222, osc.lookup("multiple").getPort());
158 assertEquals(2222, osc.lookup("quoted").getPort());
159 assertEquals(2222, osc.lookup("and").getPort());
160 assertEquals(2222, osc.lookup("unquoted").getPort());
161 assertEquals(2222, osc.lookup("hosts").getPort());
162 assertEquals(" spaced\ttld ", osc.lookup("spaced").getHostName());
163 assertEquals("bad.tld\"", osc.lookup("bad").getHostName());
164 }
165
166 @Test
167 public void testAlias_DoesNotMatch() throws Exception {
168 config("Host orcz\n" + "Port 29418\n" + "\tHostName repo.or.cz\n");
169 final Host h = osc.lookup("repo.or.cz");
170 assertNotNull(h);
171 assertEquals("repo.or.cz", h.getHostName());
172 assertEquals("jex_junit", h.getUser());
173 assertEquals(22, h.getPort());
174 assertNull(h.getIdentityFile());
175 final Host h2 = osc.lookup("orcz");
176 assertEquals("repo.or.cz", h.getHostName());
177 assertEquals("jex_junit", h.getUser());
178 assertEquals(29418, h2.getPort());
179 assertNull(h.getIdentityFile());
180 }
181
182 @Test
183 public void testAlias_OptionsSet() throws Exception {
184 config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\tPort 2222\n"
185 + "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n"
186 + "\tForwardX11 no\n");
187 final Host h = osc.lookup("orcz");
188 assertNotNull(h);
189 assertEquals("repo.or.cz", h.getHostName());
190 assertEquals("jex", h.getUser());
191 assertEquals(2222, h.getPort());
192 assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
193 }
194
195 @Test
196 public void testAlias_OptionsKeywordCaseInsensitive() throws Exception {
197 config("hOsT orcz\n" + "\thOsTnAmE repo.or.cz\n" + "\tPORT 2222\n"
198 + "\tuser jex\n" + "\tidentityfile .ssh/id_jex\n"
199 + "\tForwardX11 no\n");
200 final Host h = osc.lookup("orcz");
201 assertNotNull(h);
202 assertEquals("repo.or.cz", h.getHostName());
203 assertEquals("jex", h.getUser());
204 assertEquals(2222, h.getPort());
205 assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
206 }
207
208 @Test
209 public void testAlias_OptionsInherit() throws Exception {
210 config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
211 + "\tHostName not.a.host.example.com\n" + "\tPort 2222\n"
212 + "\tUser jex\n" + "\tIdentityFile .ssh/id_jex\n"
213 + "\tForwardX11 no\n");
214 final Host h = osc.lookup("orcz");
215 assertNotNull(h);
216 assertEquals("repo.or.cz", h.getHostName());
217 assertEquals("jex", h.getUser());
218 assertEquals(2222, h.getPort());
219 assertEquals(new File(home, ".ssh/id_jex"), h.getIdentityFile());
220 }
221
222 @Test
223 public void testAlias_PreferredAuthenticationsDefault() throws Exception {
224 final Host h = osc.lookup("orcz");
225 assertNotNull(h);
226 assertNull(h.getPreferredAuthentications());
227 }
228
229 @Test
230 public void testAlias_PreferredAuthentications() throws Exception {
231 config("Host orcz\n" + "\tPreferredAuthentications publickey\n");
232 final Host h = osc.lookup("orcz");
233 assertNotNull(h);
234 assertEquals("publickey", h.getPreferredAuthentications());
235 }
236
237 @Test
238 public void testAlias_InheritPreferredAuthentications() throws Exception {
239 config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
240 + "\tPreferredAuthentications publickey, hostbased\n");
241 final Host h = osc.lookup("orcz");
242 assertNotNull(h);
243 assertEquals("publickey,hostbased", h.getPreferredAuthentications());
244 }
245
246 @Test
247 public void testAlias_BatchModeDefault() throws Exception {
248 final Host h = osc.lookup("orcz");
249 assertNotNull(h);
250 assertFalse(h.isBatchMode());
251 }
252
253 @Test
254 public void testAlias_BatchModeYes() throws Exception {
255 config("Host orcz\n" + "\tBatchMode yes\n");
256 final Host h = osc.lookup("orcz");
257 assertNotNull(h);
258 assertTrue(h.isBatchMode());
259 }
260
261 @Test
262 public void testAlias_InheritBatchMode() throws Exception {
263 config("Host orcz\n" + "\tHostName repo.or.cz\n" + "\n" + "Host *\n"
264 + "\tBatchMode yes\n");
265 final Host h = osc.lookup("orcz");
266 assertNotNull(h);
267 assertTrue(h.isBatchMode());
268 }
269
270 @Test
271 public void testAlias_ConnectionAttemptsDefault() throws Exception {
272 final Host h = osc.lookup("orcz");
273 assertNotNull(h);
274 assertEquals(1, h.getConnectionAttempts());
275 }
276
277 @Test
278 public void testAlias_ConnectionAttempts() throws Exception {
279 config("Host orcz\n" + "\tConnectionAttempts 5\n");
280 final Host h = osc.lookup("orcz");
281 assertNotNull(h);
282 assertEquals(5, h.getConnectionAttempts());
283 }
284
285 @Test
286 public void testAlias_invalidConnectionAttempts() throws Exception {
287 config("Host orcz\n" + "\tConnectionAttempts -1\n");
288 final Host h = osc.lookup("orcz");
289 assertNotNull(h);
290 assertEquals(1, h.getConnectionAttempts());
291 }
292
293 @Test
294 public void testAlias_badConnectionAttempts() throws Exception {
295 config("Host orcz\n" + "\tConnectionAttempts xxx\n");
296 final Host h = osc.lookup("orcz");
297 assertNotNull(h);
298 assertEquals(1, h.getConnectionAttempts());
299 }
300
301 @Test
302 public void testDefaultBlock() throws Exception {
303 config("ConnectionAttempts 5\n\nHost orcz\nConnectionAttempts 3\n");
304 final Host h = osc.lookup("orcz");
305 assertNotNull(h);
306 assertEquals(5, h.getConnectionAttempts());
307 }
308
309 @Test
310 public void testHostCaseInsensitive() throws Exception {
311 config("hOsT orcz\nConnectionAttempts 3\n");
312 final Host h = osc.lookup("orcz");
313 assertNotNull(h);
314 assertEquals(3, h.getConnectionAttempts());
315 }
316
317 @Test
318 public void testListValueSingle() throws Exception {
319 config("Host orcz\nUserKnownHostsFile /foo/bar\n");
320 final ConfigRepository.Config c = osc.getConfig("orcz");
321 assertNotNull(c);
322 assertEquals("/foo/bar", c.getValue("UserKnownHostsFile"));
323 }
324
325 @Test
326 public void testListValueMultiple() throws Exception {
327
328 config("Host orcz\nUserKnownHostsFile \"~/foo/ba z\" /foo/bar \n");
329 final ConfigRepository.Config c = osc.getConfig("orcz");
330 assertNotNull(c);
331 assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
332 "/foo/bar" },
333 c.getValues("UserKnownHostsFile"));
334 }
335
336 @Test
337 public void testRepeatedLookups() throws Exception {
338 config("Host orcz\n" + "\tConnectionAttempts 5\n");
339 final Host h1 = osc.lookup("orcz");
340 final Host h2 = osc.lookup("orcz");
341 assertNotNull(h1);
342 assertSame(h1, h2);
343 assertEquals(5, h1.getConnectionAttempts());
344 assertEquals(h1.getConnectionAttempts(), h2.getConnectionAttempts());
345 final ConfigRepository.Config c = osc.getConfig("orcz");
346 assertNotNull(c);
347 assertSame(c, h1.getConfig());
348 assertSame(c, h2.getConfig());
349 }
350
351 @Test
352 public void testRepeatedLookupsWithModification() throws Exception {
353 config("Host orcz\n" + "\tConnectionAttempts -1\n");
354 final Host h1 = osc.lookup("orcz");
355 assertNotNull(h1);
356 assertEquals(1, h1.getConnectionAttempts());
357 config("Host orcz\n" + "\tConnectionAttempts 5\n");
358 final Host h2 = osc.lookup("orcz");
359 assertNotNull(h2);
360 assertNotSame(h1, h2);
361 assertEquals(5, h2.getConnectionAttempts());
362 assertEquals(1, h1.getConnectionAttempts());
363 assertNotSame(h1.getConfig(), h2.getConfig());
364 }
365
366 @Test
367 public void testIdentityFile() throws Exception {
368 config("Host orcz\nIdentityFile \"~/foo/ba z\"\nIdentityFile /foo/bar");
369 final Host h = osc.lookup("orcz");
370 assertNotNull(h);
371 File f = h.getIdentityFile();
372 assertNotNull(f);
373
374 assertEquals(new File(home, "foo/ba z"), f);
375 final ConfigRepository.Config c = h.getConfig();
376
377 assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
378 "/foo/bar" },
379 c.getValues("IdentityFile"));
380 }
381
382 @Test
383 public void testMultiIdentityFile() throws Exception {
384 config("IdentityFile \"~/foo/ba z\"\nHost orcz\nIdentityFile /foo/bar\nHOST *\nIdentityFile /foo/baz");
385 final Host h = osc.lookup("orcz");
386 assertNotNull(h);
387 File f = h.getIdentityFile();
388 assertNotNull(f);
389
390 assertEquals(new File(home, "foo/ba z"), f);
391 final ConfigRepository.Config c = h.getConfig();
392
393 assertArrayEquals(new Object[] { new File(home, "foo/ba z").getPath(),
394 "/foo/bar", "/foo/baz" },
395 c.getValues("IdentityFile"));
396 }
397
398 @Test
399 public void testNegatedPattern() throws Exception {
400 config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST !*.or.cz\nIdentityFile /foo/baz");
401 final Host h = osc.lookup("repo.or.cz");
402 assertNotNull(h);
403 assertEquals(new File(home, "foo/bar"), h.getIdentityFile());
404 assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath() },
405 h.getConfig().getValues("IdentityFile"));
406 }
407
408 @Test
409 public void testPattern() throws Exception {
410 config("Host repo.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz");
411 final Host h = osc.lookup("repo.or.cz");
412 assertNotNull(h);
413 assertEquals(new File(home, "foo/bar"), h.getIdentityFile());
414 assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath(),
415 "/foo/baz" },
416 h.getConfig().getValues("IdentityFile"));
417 }
418
419 @Test
420 public void testMultiHost() throws Exception {
421 config("Host orcz *.or.cz\nIdentityFile ~/foo/bar\nHOST *.or.cz\nIdentityFile /foo/baz");
422 final Host h1 = osc.lookup("repo.or.cz");
423 assertNotNull(h1);
424 assertEquals(new File(home, "foo/bar"), h1.getIdentityFile());
425 assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath(),
426 "/foo/baz" },
427 h1.getConfig().getValues("IdentityFile"));
428 final Host h2 = osc.lookup("orcz");
429 assertNotNull(h2);
430 assertEquals(new File(home, "foo/bar"), h2.getIdentityFile());
431 assertArrayEquals(new Object[] { new File(home, "foo/bar").getPath() },
432 h2.getConfig().getValues("IdentityFile"));
433 }
434
435 @Test
436 public void testEqualsSign() throws Exception {
437 config("Host=orcz\n\tConnectionAttempts = 5\n\tUser=\t foobar\t\n");
438 final Host h = osc.lookup("orcz");
439 assertNotNull(h);
440 assertEquals(5, h.getConnectionAttempts());
441 assertEquals("foobar", h.getUser());
442 }
443
444 @Test
445 public void testMissingArgument() throws Exception {
446 config("Host=orcz\n\tSendEnv\nIdentityFile\t\nForwardX11\n\tUser=\t foobar\t\n");
447 final Host h = osc.lookup("orcz");
448 assertNotNull(h);
449 assertEquals("foobar", h.getUser());
450 assertArrayEquals(new String[0], h.getConfig().getValues("SendEnv"));
451 assertNull(h.getIdentityFile());
452 assertNull(h.getConfig().getValue("ForwardX11"));
453 }
454
455 @Test
456 public void testHomeDirUserReplacement() throws Exception {
457 config("Host=orcz\n\tIdentityFile %d/.ssh/%u_id_dsa");
458 final Host h = osc.lookup("orcz");
459 assertNotNull(h);
460 assertEquals(new File(new File(home, ".ssh"), "jex_junit_id_dsa"),
461 h.getIdentityFile());
462 }
463
464 @Test
465 public void testHostnameReplacement() throws Exception {
466 config("Host=orcz\nHost *.*\n\tHostname %h\nHost *\n\tHostname %h.example.org");
467 final Host h = osc.lookup("orcz");
468 assertNotNull(h);
469 assertEquals("orcz.example.org", h.getHostName());
470 }
471
472 @Test
473 public void testRemoteUserReplacement() throws Exception {
474 config("Host=orcz\n\tUser foo\n" + "Host *.*\n\tHostname %h\n"
475 + "Host *\n\tHostname %h.ex%%20ample.org\n\tIdentityFile ~/.ssh/%h_%r_id_dsa");
476 final Host h = osc.lookup("orcz");
477 assertNotNull(h);
478 assertEquals(
479 new File(new File(home, ".ssh"),
480 "orcz.ex%20ample.org_foo_id_dsa"),
481 h.getIdentityFile());
482 }
483
484 @Test
485 public void testLocalhostFQDNReplacement() throws Exception {
486 String localhost = SystemReader.getInstance().getHostname();
487 config("Host=orcz\n\tIdentityFile ~/.ssh/%l_id_dsa");
488 final Host h = osc.lookup("orcz");
489 assertNotNull(h);
490 assertEquals(
491 new File(new File(home, ".ssh"), localhost + "_id_dsa"),
492 h.getIdentityFile());
493 }
494 }