Merge branch 'jk/for-each-ref-multi-key-sort-fix'
[git] / t / t0300-credentials.sh
1 #!/bin/sh
2
3 test_description='basic credential helper tests'
4 . ./test-lib.sh
5 . "$TEST_DIRECTORY"/lib-credential.sh
6
7 test_expect_success 'setup helper scripts' '
8         cat >dump <<-\EOF &&
9         whoami=$(echo $0 | sed s/.*git-credential-//)
10         echo >&2 "$whoami: $*"
11         OIFS=$IFS
12         IFS==
13         while read key value; do
14                 echo >&2 "$whoami: $key=$value"
15                 eval "$key=$value"
16         done
17         IFS=$OIFS
18         EOF
19
20         write_script git-credential-useless <<-\EOF &&
21         . ./dump
22         exit 0
23         EOF
24
25         write_script git-credential-quit <<-\EOF &&
26         . ./dump
27         echo quit=1
28         EOF
29
30         write_script git-credential-verbatim <<-\EOF &&
31         user=$1; shift
32         pass=$1; shift
33         . ./dump
34         test -z "$user" || echo username=$user
35         test -z "$pass" || echo password=$pass
36         EOF
37
38         PATH="$PWD:$PATH"
39 '
40
41 test_expect_success 'credential_fill invokes helper' '
42         check fill "verbatim foo bar" <<-\EOF
43         protocol=http
44         host=example.com
45         --
46         protocol=http
47         host=example.com
48         username=foo
49         password=bar
50         --
51         verbatim: get
52         verbatim: protocol=http
53         verbatim: host=example.com
54         EOF
55 '
56
57 test_expect_success 'credential_fill invokes multiple helpers' '
58         check fill useless "verbatim foo bar" <<-\EOF
59         protocol=http
60         host=example.com
61         --
62         protocol=http
63         host=example.com
64         username=foo
65         password=bar
66         --
67         useless: get
68         useless: protocol=http
69         useless: host=example.com
70         verbatim: get
71         verbatim: protocol=http
72         verbatim: host=example.com
73         EOF
74 '
75
76 test_expect_success 'credential_fill stops when we get a full response' '
77         check fill "verbatim one two" "verbatim three four" <<-\EOF
78         protocol=http
79         host=example.com
80         --
81         protocol=http
82         host=example.com
83         username=one
84         password=two
85         --
86         verbatim: get
87         verbatim: protocol=http
88         verbatim: host=example.com
89         EOF
90 '
91
92 test_expect_success 'credential_fill continues through partial response' '
93         check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
94         protocol=http
95         host=example.com
96         --
97         protocol=http
98         host=example.com
99         username=two
100         password=three
101         --
102         verbatim: get
103         verbatim: protocol=http
104         verbatim: host=example.com
105         verbatim: get
106         verbatim: protocol=http
107         verbatim: host=example.com
108         verbatim: username=one
109         EOF
110 '
111
112 test_expect_success 'credential_fill passes along metadata' '
113         check fill "verbatim one two" <<-\EOF
114         protocol=ftp
115         host=example.com
116         path=foo.git
117         --
118         protocol=ftp
119         host=example.com
120         path=foo.git
121         username=one
122         password=two
123         --
124         verbatim: get
125         verbatim: protocol=ftp
126         verbatim: host=example.com
127         verbatim: path=foo.git
128         EOF
129 '
130
131 test_expect_success 'credential_approve calls all helpers' '
132         check approve useless "verbatim one two" <<-\EOF
133         protocol=http
134         host=example.com
135         username=foo
136         password=bar
137         --
138         --
139         useless: store
140         useless: protocol=http
141         useless: host=example.com
142         useless: username=foo
143         useless: password=bar
144         verbatim: store
145         verbatim: protocol=http
146         verbatim: host=example.com
147         verbatim: username=foo
148         verbatim: password=bar
149         EOF
150 '
151
152 test_expect_success 'do not bother storing password-less credential' '
153         check approve useless <<-\EOF
154         protocol=http
155         host=example.com
156         username=foo
157         --
158         --
159         EOF
160 '
161
162
163 test_expect_success 'credential_reject calls all helpers' '
164         check reject useless "verbatim one two" <<-\EOF
165         protocol=http
166         host=example.com
167         username=foo
168         password=bar
169         --
170         --
171         useless: erase
172         useless: protocol=http
173         useless: host=example.com
174         useless: username=foo
175         useless: password=bar
176         verbatim: erase
177         verbatim: protocol=http
178         verbatim: host=example.com
179         verbatim: username=foo
180         verbatim: password=bar
181         EOF
182 '
183
184 test_expect_success 'usernames can be preserved' '
185         check fill "verbatim \"\" three" <<-\EOF
186         protocol=http
187         host=example.com
188         username=one
189         --
190         protocol=http
191         host=example.com
192         username=one
193         password=three
194         --
195         verbatim: get
196         verbatim: protocol=http
197         verbatim: host=example.com
198         verbatim: username=one
199         EOF
200 '
201
202 test_expect_success 'usernames can be overridden' '
203         check fill "verbatim two three" <<-\EOF
204         protocol=http
205         host=example.com
206         username=one
207         --
208         protocol=http
209         host=example.com
210         username=two
211         password=three
212         --
213         verbatim: get
214         verbatim: protocol=http
215         verbatim: host=example.com
216         verbatim: username=one
217         EOF
218 '
219
220 test_expect_success 'do not bother completing already-full credential' '
221         check fill "verbatim three four" <<-\EOF
222         protocol=http
223         host=example.com
224         username=one
225         password=two
226         --
227         protocol=http
228         host=example.com
229         username=one
230         password=two
231         --
232         EOF
233 '
234
235 # We can't test the basic terminal password prompt here because
236 # getpass() tries too hard to find the real terminal. But if our
237 # askpass helper is run, we know the internal getpass is working.
238 test_expect_success 'empty helper list falls back to internal getpass' '
239         check fill <<-\EOF
240         protocol=http
241         host=example.com
242         --
243         protocol=http
244         host=example.com
245         username=askpass-username
246         password=askpass-password
247         --
248         askpass: Username for '\''http://example.com'\'':
249         askpass: Password for '\''http://askpass-username@example.com'\'':
250         EOF
251 '
252
253 test_expect_success 'internal getpass does not ask for known username' '
254         check fill <<-\EOF
255         protocol=http
256         host=example.com
257         username=foo
258         --
259         protocol=http
260         host=example.com
261         username=foo
262         password=askpass-password
263         --
264         askpass: Password for '\''http://foo@example.com'\'':
265         EOF
266 '
267
268 HELPER="!f() {
269                 cat >/dev/null
270                 echo username=foo
271                 echo password=bar
272         }; f"
273 test_expect_success 'respect configured credentials' '
274         test_config credential.helper "$HELPER" &&
275         check fill <<-\EOF
276         protocol=http
277         host=example.com
278         --
279         protocol=http
280         host=example.com
281         username=foo
282         password=bar
283         --
284         EOF
285 '
286
287 test_expect_success 'match configured credential' '
288         test_config credential.https://example.com.helper "$HELPER" &&
289         check fill <<-\EOF
290         protocol=https
291         host=example.com
292         path=repo.git
293         --
294         protocol=https
295         host=example.com
296         username=foo
297         password=bar
298         --
299         EOF
300 '
301
302 test_expect_success 'do not match configured credential' '
303         test_config credential.https://foo.helper "$HELPER" &&
304         check fill <<-\EOF
305         protocol=https
306         host=bar
307         --
308         protocol=https
309         host=bar
310         username=askpass-username
311         password=askpass-password
312         --
313         askpass: Username for '\''https://bar'\'':
314         askpass: Password for '\''https://askpass-username@bar'\'':
315         EOF
316 '
317
318 test_expect_success 'match multiple configured helpers' '
319         test_config credential.helper "verbatim \"\" \"\"" &&
320         test_config credential.https://example.com.helper "$HELPER" &&
321         check fill <<-\EOF
322         protocol=https
323         host=example.com
324         path=repo.git
325         --
326         protocol=https
327         host=example.com
328         username=foo
329         password=bar
330         --
331         verbatim: get
332         verbatim: protocol=https
333         verbatim: host=example.com
334         EOF
335 '
336
337 test_expect_success 'match multiple configured helpers with URLs' '
338         test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
339         test_config credential.https://example.com.helper "$HELPER" &&
340         check fill <<-\EOF
341         protocol=https
342         host=example.com
343         path=repo.git
344         --
345         protocol=https
346         host=example.com
347         username=foo
348         password=bar
349         --
350         verbatim: get
351         verbatim: protocol=https
352         verbatim: host=example.com
353         EOF
354 '
355
356 test_expect_success 'match percent-encoded values' '
357         test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
358         check fill <<-\EOF
359         url=https://example.com/%2566.git
360         --
361         protocol=https
362         host=example.com
363         username=foo
364         password=bar
365         --
366         EOF
367 '
368
369 test_expect_success 'match percent-encoded UTF-8 values in path' '
370         test_config credential.https://example.com.useHttpPath true &&
371         test_config credential.https://example.com/perú.git.helper "$HELPER" &&
372         check fill <<-\EOF
373         url=https://example.com/per%C3%BA.git
374         --
375         protocol=https
376         host=example.com
377         path=perú.git
378         username=foo
379         password=bar
380         --
381         EOF
382 '
383
384 test_expect_success 'match percent-encoded values in username' '
385         test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
386         check fill <<-\EOF
387         url=https://user%2fname@example.com/foo/bar.git
388         --
389         protocol=https
390         host=example.com
391         username=foo
392         password=bar
393         --
394         EOF
395 '
396
397 test_expect_success 'fetch with multiple path components' '
398         test_unconfig credential.helper &&
399         test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&
400         check fill <<-\EOF
401         url=https://example.com/foo/repo.git
402         --
403         protocol=https
404         host=example.com
405         username=foo
406         password=bar
407         --
408         verbatim: get
409         verbatim: protocol=https
410         verbatim: host=example.com
411         EOF
412 '
413
414 test_expect_success 'pull username from config' '
415         test_config credential.https://example.com.username foo &&
416         check fill <<-\EOF
417         protocol=https
418         host=example.com
419         --
420         protocol=https
421         host=example.com
422         username=foo
423         password=askpass-password
424         --
425         askpass: Password for '\''https://foo@example.com'\'':
426         EOF
427 '
428
429 test_expect_success 'honors username from URL over helper (URL)' '
430         test_config credential.https://example.com.username bob &&
431         test_config credential.https://example.com.helper "verbatim \"\" bar" &&
432         check fill <<-\EOF
433         url=https://alice@example.com
434         --
435         protocol=https
436         host=example.com
437         username=alice
438         password=bar
439         --
440         verbatim: get
441         verbatim: protocol=https
442         verbatim: host=example.com
443         verbatim: username=alice
444         EOF
445 '
446
447 test_expect_success 'honors username from URL over helper (components)' '
448         test_config credential.https://example.com.username bob &&
449         test_config credential.https://example.com.helper "verbatim \"\" bar" &&
450         check fill <<-\EOF
451         protocol=https
452         host=example.com
453         username=alice
454         --
455         protocol=https
456         host=example.com
457         username=alice
458         password=bar
459         --
460         verbatim: get
461         verbatim: protocol=https
462         verbatim: host=example.com
463         verbatim: username=alice
464         EOF
465 '
466
467 test_expect_success 'last matching username wins' '
468         test_config credential.https://example.com/path.git.username bob &&
469         test_config credential.https://example.com.username alice &&
470         test_config credential.https://example.com.helper "verbatim \"\" bar" &&
471         check fill <<-\EOF
472         url=https://example.com/path.git
473         --
474         protocol=https
475         host=example.com
476         username=alice
477         password=bar
478         --
479         verbatim: get
480         verbatim: protocol=https
481         verbatim: host=example.com
482         verbatim: username=alice
483         EOF
484 '
485
486 test_expect_success 'http paths can be part of context' '
487         check fill "verbatim foo bar" <<-\EOF &&
488         protocol=https
489         host=example.com
490         path=foo.git
491         --
492         protocol=https
493         host=example.com
494         username=foo
495         password=bar
496         --
497         verbatim: get
498         verbatim: protocol=https
499         verbatim: host=example.com
500         EOF
501         test_config credential.https://example.com.useHttpPath true &&
502         check fill "verbatim foo bar" <<-\EOF
503         protocol=https
504         host=example.com
505         path=foo.git
506         --
507         protocol=https
508         host=example.com
509         path=foo.git
510         username=foo
511         password=bar
512         --
513         verbatim: get
514         verbatim: protocol=https
515         verbatim: host=example.com
516         verbatim: path=foo.git
517         EOF
518 '
519
520 test_expect_success 'context uses urlmatch' '
521         test_config "credential.https://*.org.useHttpPath" true &&
522         check fill "verbatim foo bar" <<-\EOF
523         protocol=https
524         host=example.org
525         path=foo.git
526         --
527         protocol=https
528         host=example.org
529         path=foo.git
530         username=foo
531         password=bar
532         --
533         verbatim: get
534         verbatim: protocol=https
535         verbatim: host=example.org
536         verbatim: path=foo.git
537         EOF
538 '
539
540 test_expect_success 'helpers can abort the process' '
541         test_must_fail git \
542                 -c credential.helper=quit \
543                 -c credential.helper="verbatim foo bar" \
544                 credential fill >stdout 2>stderr <<-\EOF &&
545         protocol=http
546         host=example.com
547         EOF
548         test_must_be_empty stdout &&
549         cat >expect <<-\EOF &&
550         quit: get
551         quit: protocol=http
552         quit: host=example.com
553         fatal: credential helper '\''quit'\'' told us to quit
554         EOF
555         test_i18ncmp expect stderr
556 '
557
558 test_expect_success 'empty helper spec resets helper list' '
559         test_config credential.helper "verbatim file file" &&
560         check fill "" "verbatim cmdline cmdline" <<-\EOF
561         protocol=http
562         host=example.com
563         --
564         protocol=http
565         host=example.com
566         username=cmdline
567         password=cmdline
568         --
569         verbatim: get
570         verbatim: protocol=http
571         verbatim: host=example.com
572         EOF
573 '
574
575 test_expect_success 'url parser rejects embedded newlines' '
576         test_must_fail git credential fill 2>stderr <<-\EOF &&
577         url=https://one.example.com?%0ahost=two.example.com/
578         EOF
579         cat >expect <<-\EOF &&
580         warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
581         fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
582         EOF
583         test_i18ncmp expect stderr
584 '
585
586 test_expect_success 'host-less URLs are parsed as empty host' '
587         check fill "verbatim foo bar" <<-\EOF
588         url=cert:///path/to/cert.pem
589         --
590         protocol=cert
591         host=
592         path=path/to/cert.pem
593         username=foo
594         password=bar
595         --
596         verbatim: get
597         verbatim: protocol=cert
598         verbatim: host=
599         verbatim: path=path/to/cert.pem
600         EOF
601 '
602
603 test_expect_success 'credential system refuses to work with missing host' '
604         test_must_fail git credential fill 2>stderr <<-\EOF &&
605         protocol=http
606         EOF
607         cat >expect <<-\EOF &&
608         fatal: refusing to work with credential missing host field
609         EOF
610         test_i18ncmp expect stderr
611 '
612
613 test_expect_success 'credential system refuses to work with missing protocol' '
614         test_must_fail git credential fill 2>stderr <<-\EOF &&
615         host=example.com
616         EOF
617         cat >expect <<-\EOF &&
618         fatal: refusing to work with credential missing protocol field
619         EOF
620         test_i18ncmp expect stderr
621 '
622
623 # usage: check_host_and_path <url> <expected-host> <expected-path>
624 check_host_and_path () {
625         # we always parse the path component, but we need this to make sure it
626         # is passed to the helper
627         test_config credential.useHTTPPath true &&
628         check fill "verbatim user pass" <<-EOF
629         url=$1
630         --
631         protocol=https
632         host=$2
633         path=$3
634         username=user
635         password=pass
636         --
637         verbatim: get
638         verbatim: protocol=https
639         verbatim: host=$2
640         verbatim: path=$3
641         EOF
642 }
643
644 test_expect_success 'url parser handles bare query marker' '
645         check_host_and_path https://example.com?foo.git example.com ?foo.git
646 '
647
648 test_expect_success 'url parser handles bare fragment marker' '
649         check_host_and_path https://example.com#foo.git example.com "#foo.git"
650 '
651
652 test_expect_success 'url parser not confused by encoded markers' '
653         check_host_and_path https://example.com%23%3f%2f/foo.git \
654                 "example.com#?/" foo.git
655 '
656
657 test_expect_success 'credential config with partial URLs' '
658         echo "echo password=yep" | write_script git-credential-yep &&
659         test_write_lines url=https://user@example.com/repo.git >stdin &&
660         for partial in \
661                 example.com \
662                 user@example.com \
663                 https:// \
664                 https://example.com \
665                 https://example.com/ \
666                 https://user@example.com \
667                 https://user@example.com/ \
668                 https://example.com/repo.git \
669                 https://user@example.com/repo.git \
670                 /repo.git
671         do
672                 git -c credential.$partial.helper=yep \
673                         credential fill <stdin >stdout &&
674                 grep yep stdout ||
675                 return 1
676         done &&
677
678         for partial in \
679                 dont.use.this \
680                 http:// \
681                 /repo
682         do
683                 git -c credential.$partial.helper=yep \
684                         credential fill <stdin >stdout &&
685                 ! grep yep stdout ||
686                 return 1
687         done &&
688
689         git -c credential.$partial.helper=yep \
690                 -c credential.with%0anewline.username=uh-oh \
691                 credential fill <stdin >stdout 2>stderr &&
692         test_i18ngrep "skipping credential lookup for key" stderr
693 '
694
695 test_done