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