The second batch
[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 test_expect_success 'git-credential respects core.askPass' '
269         write_script alternate-askpass <<-\EOF &&
270         echo >&2 "alternate askpass invoked"
271         echo alternate-value
272         EOF
273         test_config core.askpass "$PWD/alternate-askpass" &&
274         (
275                 # unset GIT_ASKPASS set by lib-credential.sh which would
276                 # override our config, but do so in a subshell so that we do
277                 # not interfere with other tests
278                 sane_unset GIT_ASKPASS &&
279                 check fill <<-\EOF
280                 protocol=http
281                 host=example.com
282                 --
283                 protocol=http
284                 host=example.com
285                 username=alternate-value
286                 password=alternate-value
287                 --
288                 alternate askpass invoked
289                 alternate askpass invoked
290                 EOF
291         )
292 '
293
294 HELPER="!f() {
295                 cat >/dev/null
296                 echo username=foo
297                 echo password=bar
298         }; f"
299 test_expect_success 'respect configured credentials' '
300         test_config credential.helper "$HELPER" &&
301         check fill <<-\EOF
302         protocol=http
303         host=example.com
304         --
305         protocol=http
306         host=example.com
307         username=foo
308         password=bar
309         --
310         EOF
311 '
312
313 test_expect_success 'match configured credential' '
314         test_config credential.https://example.com.helper "$HELPER" &&
315         check fill <<-\EOF
316         protocol=https
317         host=example.com
318         path=repo.git
319         --
320         protocol=https
321         host=example.com
322         username=foo
323         password=bar
324         --
325         EOF
326 '
327
328 test_expect_success 'do not match configured credential' '
329         test_config credential.https://foo.helper "$HELPER" &&
330         check fill <<-\EOF
331         protocol=https
332         host=bar
333         --
334         protocol=https
335         host=bar
336         username=askpass-username
337         password=askpass-password
338         --
339         askpass: Username for '\''https://bar'\'':
340         askpass: Password for '\''https://askpass-username@bar'\'':
341         EOF
342 '
343
344 test_expect_success 'match multiple configured helpers' '
345         test_config credential.helper "verbatim \"\" \"\"" &&
346         test_config credential.https://example.com.helper "$HELPER" &&
347         check fill <<-\EOF
348         protocol=https
349         host=example.com
350         path=repo.git
351         --
352         protocol=https
353         host=example.com
354         username=foo
355         password=bar
356         --
357         verbatim: get
358         verbatim: protocol=https
359         verbatim: host=example.com
360         EOF
361 '
362
363 test_expect_success 'match multiple configured helpers with URLs' '
364         test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
365         test_config credential.https://example.com.helper "$HELPER" &&
366         check fill <<-\EOF
367         protocol=https
368         host=example.com
369         path=repo.git
370         --
371         protocol=https
372         host=example.com
373         username=foo
374         password=bar
375         --
376         verbatim: get
377         verbatim: protocol=https
378         verbatim: host=example.com
379         EOF
380 '
381
382 test_expect_success 'match percent-encoded values' '
383         test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
384         check fill <<-\EOF
385         url=https://example.com/%2566.git
386         --
387         protocol=https
388         host=example.com
389         username=foo
390         password=bar
391         --
392         EOF
393 '
394
395 test_expect_success 'match percent-encoded UTF-8 values in path' '
396         test_config credential.https://example.com.useHttpPath true &&
397         test_config credential.https://example.com/perĂº.git.helper "$HELPER" &&
398         check fill <<-\EOF
399         url=https://example.com/per%C3%BA.git
400         --
401         protocol=https
402         host=example.com
403         path=perĂº.git
404         username=foo
405         password=bar
406         --
407         EOF
408 '
409
410 test_expect_success 'match percent-encoded values in username' '
411         test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
412         check fill <<-\EOF
413         url=https://user%2fname@example.com/foo/bar.git
414         --
415         protocol=https
416         host=example.com
417         username=foo
418         password=bar
419         --
420         EOF
421 '
422
423 test_expect_success 'fetch with multiple path components' '
424         test_unconfig credential.helper &&
425         test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&
426         check fill <<-\EOF
427         url=https://example.com/foo/repo.git
428         --
429         protocol=https
430         host=example.com
431         username=foo
432         password=bar
433         --
434         verbatim: get
435         verbatim: protocol=https
436         verbatim: host=example.com
437         EOF
438 '
439
440 test_expect_success 'pull username from config' '
441         test_config credential.https://example.com.username foo &&
442         check fill <<-\EOF
443         protocol=https
444         host=example.com
445         --
446         protocol=https
447         host=example.com
448         username=foo
449         password=askpass-password
450         --
451         askpass: Password for '\''https://foo@example.com'\'':
452         EOF
453 '
454
455 test_expect_success 'honors username from URL over helper (URL)' '
456         test_config credential.https://example.com.username bob &&
457         test_config credential.https://example.com.helper "verbatim \"\" bar" &&
458         check fill <<-\EOF
459         url=https://alice@example.com
460         --
461         protocol=https
462         host=example.com
463         username=alice
464         password=bar
465         --
466         verbatim: get
467         verbatim: protocol=https
468         verbatim: host=example.com
469         verbatim: username=alice
470         EOF
471 '
472
473 test_expect_success 'honors username from URL over helper (components)' '
474         test_config credential.https://example.com.username bob &&
475         test_config credential.https://example.com.helper "verbatim \"\" bar" &&
476         check fill <<-\EOF
477         protocol=https
478         host=example.com
479         username=alice
480         --
481         protocol=https
482         host=example.com
483         username=alice
484         password=bar
485         --
486         verbatim: get
487         verbatim: protocol=https
488         verbatim: host=example.com
489         verbatim: username=alice
490         EOF
491 '
492
493 test_expect_success 'last matching username wins' '
494         test_config credential.https://example.com/path.git.username bob &&
495         test_config credential.https://example.com.username alice &&
496         test_config credential.https://example.com.helper "verbatim \"\" bar" &&
497         check fill <<-\EOF
498         url=https://example.com/path.git
499         --
500         protocol=https
501         host=example.com
502         username=alice
503         password=bar
504         --
505         verbatim: get
506         verbatim: protocol=https
507         verbatim: host=example.com
508         verbatim: username=alice
509         EOF
510 '
511
512 test_expect_success 'http paths can be part of context' '
513         check fill "verbatim foo bar" <<-\EOF &&
514         protocol=https
515         host=example.com
516         path=foo.git
517         --
518         protocol=https
519         host=example.com
520         username=foo
521         password=bar
522         --
523         verbatim: get
524         verbatim: protocol=https
525         verbatim: host=example.com
526         EOF
527         test_config credential.https://example.com.useHttpPath true &&
528         check fill "verbatim foo bar" <<-\EOF
529         protocol=https
530         host=example.com
531         path=foo.git
532         --
533         protocol=https
534         host=example.com
535         path=foo.git
536         username=foo
537         password=bar
538         --
539         verbatim: get
540         verbatim: protocol=https
541         verbatim: host=example.com
542         verbatim: path=foo.git
543         EOF
544 '
545
546 test_expect_success 'context uses urlmatch' '
547         test_config "credential.https://*.org.useHttpPath" true &&
548         check fill "verbatim foo bar" <<-\EOF
549         protocol=https
550         host=example.org
551         path=foo.git
552         --
553         protocol=https
554         host=example.org
555         path=foo.git
556         username=foo
557         password=bar
558         --
559         verbatim: get
560         verbatim: protocol=https
561         verbatim: host=example.org
562         verbatim: path=foo.git
563         EOF
564 '
565
566 test_expect_success 'helpers can abort the process' '
567         test_must_fail git \
568                 -c credential.helper=quit \
569                 -c credential.helper="verbatim foo bar" \
570                 credential fill >stdout 2>stderr <<-\EOF &&
571         protocol=http
572         host=example.com
573         EOF
574         test_must_be_empty stdout &&
575         cat >expect <<-\EOF &&
576         quit: get
577         quit: protocol=http
578         quit: host=example.com
579         fatal: credential helper '\''quit'\'' told us to quit
580         EOF
581         test_cmp expect stderr
582 '
583
584 test_expect_success 'empty helper spec resets helper list' '
585         test_config credential.helper "verbatim file file" &&
586         check fill "" "verbatim cmdline cmdline" <<-\EOF
587         protocol=http
588         host=example.com
589         --
590         protocol=http
591         host=example.com
592         username=cmdline
593         password=cmdline
594         --
595         verbatim: get
596         verbatim: protocol=http
597         verbatim: host=example.com
598         EOF
599 '
600
601 test_expect_success 'url parser rejects embedded newlines' '
602         test_must_fail git credential fill 2>stderr <<-\EOF &&
603         url=https://one.example.com?%0ahost=two.example.com/
604         EOF
605         cat >expect <<-\EOF &&
606         warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
607         fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
608         EOF
609         test_cmp expect stderr
610 '
611
612 test_expect_success 'host-less URLs are parsed as empty host' '
613         check fill "verbatim foo bar" <<-\EOF
614         url=cert:///path/to/cert.pem
615         --
616         protocol=cert
617         host=
618         path=path/to/cert.pem
619         username=foo
620         password=bar
621         --
622         verbatim: get
623         verbatim: protocol=cert
624         verbatim: host=
625         verbatim: path=path/to/cert.pem
626         EOF
627 '
628
629 test_expect_success 'credential system refuses to work with missing host' '
630         test_must_fail git credential fill 2>stderr <<-\EOF &&
631         protocol=http
632         EOF
633         cat >expect <<-\EOF &&
634         fatal: refusing to work with credential missing host field
635         EOF
636         test_cmp expect stderr
637 '
638
639 test_expect_success 'credential system refuses to work with missing protocol' '
640         test_must_fail git credential fill 2>stderr <<-\EOF &&
641         host=example.com
642         EOF
643         cat >expect <<-\EOF &&
644         fatal: refusing to work with credential missing protocol field
645         EOF
646         test_cmp expect stderr
647 '
648
649 # usage: check_host_and_path <url> <expected-host> <expected-path>
650 check_host_and_path () {
651         # we always parse the path component, but we need this to make sure it
652         # is passed to the helper
653         test_config credential.useHTTPPath true &&
654         check fill "verbatim user pass" <<-EOF
655         url=$1
656         --
657         protocol=https
658         host=$2
659         path=$3
660         username=user
661         password=pass
662         --
663         verbatim: get
664         verbatim: protocol=https
665         verbatim: host=$2
666         verbatim: path=$3
667         EOF
668 }
669
670 test_expect_success 'url parser handles bare query marker' '
671         check_host_and_path https://example.com?foo.git example.com ?foo.git
672 '
673
674 test_expect_success 'url parser handles bare fragment marker' '
675         check_host_and_path https://example.com#foo.git example.com "#foo.git"
676 '
677
678 test_expect_success 'url parser not confused by encoded markers' '
679         check_host_and_path https://example.com%23%3f%2f/foo.git \
680                 "example.com#?/" foo.git
681 '
682
683 test_expect_success 'credential config with partial URLs' '
684         echo "echo password=yep" | write_script git-credential-yep &&
685         test_write_lines url=https://user@example.com/repo.git >stdin &&
686         for partial in \
687                 example.com \
688                 user@example.com \
689                 https:// \
690                 https://example.com \
691                 https://example.com/ \
692                 https://user@example.com \
693                 https://user@example.com/ \
694                 https://example.com/repo.git \
695                 https://user@example.com/repo.git \
696                 /repo.git
697         do
698                 git -c credential.$partial.helper=yep \
699                         credential fill <stdin >stdout &&
700                 grep yep stdout ||
701                 return 1
702         done &&
703
704         for partial in \
705                 dont.use.this \
706                 http:// \
707                 /repo
708         do
709                 git -c credential.$partial.helper=yep \
710                         credential fill <stdin >stdout &&
711                 ! grep yep stdout ||
712                 return 1
713         done &&
714
715         git -c credential.$partial.helper=yep \
716                 -c credential.with%0anewline.username=uh-oh \
717                 credential fill <stdin >stdout 2>stderr &&
718         test_i18ngrep "skipping credential lookup for key" stderr
719 '
720
721 test_done