]> git.ipfire.org Git - thirdparty/git.git/blob - t/t0300-credentials.sh
a4f5bba5075c3d61a86b2fad1cb95ba73058bc8e
[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 'credential_approve stores oauth refresh token' '
218 check approve useless <<-\EOF
219 protocol=http
220 host=example.com
221 username=foo
222 password=bar
223 oauth_refresh_token=xyzzy
224 --
225 --
226 useless: store
227 useless: protocol=http
228 useless: host=example.com
229 useless: username=foo
230 useless: password=bar
231 useless: oauth_refresh_token=xyzzy
232 EOF
233 '
234
235 test_expect_success 'do not bother storing password-less credential' '
236 check approve useless <<-\EOF
237 protocol=http
238 host=example.com
239 username=foo
240 --
241 --
242 EOF
243 '
244
245 test_expect_success 'credential_approve does not store expired password' '
246 check approve useless <<-\EOF
247 protocol=http
248 host=example.com
249 username=foo
250 password=bar
251 password_expiry_utc=5
252 --
253 --
254 EOF
255 '
256
257 test_expect_success 'credential_reject calls all helpers' '
258 check reject useless "verbatim one two" <<-\EOF
259 protocol=http
260 host=example.com
261 username=foo
262 password=bar
263 --
264 --
265 useless: erase
266 useless: protocol=http
267 useless: host=example.com
268 useless: username=foo
269 useless: password=bar
270 verbatim: erase
271 verbatim: protocol=http
272 verbatim: host=example.com
273 verbatim: username=foo
274 verbatim: password=bar
275 EOF
276 '
277
278 test_expect_success 'credential_reject erases credential regardless of expiry' '
279 check reject useless <<-\EOF
280 protocol=http
281 host=example.com
282 username=foo
283 password=bar
284 password_expiry_utc=5
285 --
286 --
287 useless: erase
288 useless: protocol=http
289 useless: host=example.com
290 useless: username=foo
291 useless: password=bar
292 useless: password_expiry_utc=5
293 EOF
294 '
295
296 test_expect_success 'usernames can be preserved' '
297 check fill "verbatim \"\" three" <<-\EOF
298 protocol=http
299 host=example.com
300 username=one
301 --
302 protocol=http
303 host=example.com
304 username=one
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 'usernames can be overridden' '
315 check fill "verbatim two three" <<-\EOF
316 protocol=http
317 host=example.com
318 username=one
319 --
320 protocol=http
321 host=example.com
322 username=two
323 password=three
324 --
325 verbatim: get
326 verbatim: protocol=http
327 verbatim: host=example.com
328 verbatim: username=one
329 EOF
330 '
331
332 test_expect_success 'do not bother completing already-full credential' '
333 check fill "verbatim three four" <<-\EOF
334 protocol=http
335 host=example.com
336 username=one
337 password=two
338 --
339 protocol=http
340 host=example.com
341 username=one
342 password=two
343 --
344 EOF
345 '
346
347 # We can't test the basic terminal password prompt here because
348 # getpass() tries too hard to find the real terminal. But if our
349 # askpass helper is run, we know the internal getpass is working.
350 test_expect_success 'empty helper list falls back to internal getpass' '
351 check fill <<-\EOF
352 protocol=http
353 host=example.com
354 --
355 protocol=http
356 host=example.com
357 username=askpass-username
358 password=askpass-password
359 --
360 askpass: Username for '\''http://example.com'\'':
361 askpass: Password for '\''http://askpass-username@example.com'\'':
362 EOF
363 '
364
365 test_expect_success 'internal getpass does not ask for known username' '
366 check fill <<-\EOF
367 protocol=http
368 host=example.com
369 username=foo
370 --
371 protocol=http
372 host=example.com
373 username=foo
374 password=askpass-password
375 --
376 askpass: Password for '\''http://foo@example.com'\'':
377 EOF
378 '
379
380 test_expect_success 'git-credential respects core.askPass' '
381 write_script alternate-askpass <<-\EOF &&
382 echo >&2 "alternate askpass invoked"
383 echo alternate-value
384 EOF
385 test_config core.askpass "$PWD/alternate-askpass" &&
386 (
387 # unset GIT_ASKPASS set by lib-credential.sh which would
388 # override our config, but do so in a subshell so that we do
389 # not interfere with other tests
390 sane_unset GIT_ASKPASS &&
391 check fill <<-\EOF
392 protocol=http
393 host=example.com
394 --
395 protocol=http
396 host=example.com
397 username=alternate-value
398 password=alternate-value
399 --
400 alternate askpass invoked
401 alternate askpass invoked
402 EOF
403 )
404 '
405
406 HELPER="!f() {
407 cat >/dev/null
408 echo username=foo
409 echo password=bar
410 }; f"
411 test_expect_success 'respect configured credentials' '
412 test_config credential.helper "$HELPER" &&
413 check fill <<-\EOF
414 protocol=http
415 host=example.com
416 --
417 protocol=http
418 host=example.com
419 username=foo
420 password=bar
421 --
422 EOF
423 '
424
425 test_expect_success 'match configured credential' '
426 test_config credential.https://example.com.helper "$HELPER" &&
427 check fill <<-\EOF
428 protocol=https
429 host=example.com
430 path=repo.git
431 --
432 protocol=https
433 host=example.com
434 username=foo
435 password=bar
436 --
437 EOF
438 '
439
440 test_expect_success 'do not match configured credential' '
441 test_config credential.https://foo.helper "$HELPER" &&
442 check fill <<-\EOF
443 protocol=https
444 host=bar
445 --
446 protocol=https
447 host=bar
448 username=askpass-username
449 password=askpass-password
450 --
451 askpass: Username for '\''https://bar'\'':
452 askpass: Password for '\''https://askpass-username@bar'\'':
453 EOF
454 '
455
456 test_expect_success 'match multiple configured helpers' '
457 test_config credential.helper "verbatim \"\" \"\"" &&
458 test_config credential.https://example.com.helper "$HELPER" &&
459 check fill <<-\EOF
460 protocol=https
461 host=example.com
462 path=repo.git
463 --
464 protocol=https
465 host=example.com
466 username=foo
467 password=bar
468 --
469 verbatim: get
470 verbatim: protocol=https
471 verbatim: host=example.com
472 EOF
473 '
474
475 test_expect_success 'match multiple configured helpers with URLs' '
476 test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
477 test_config credential.https://example.com.helper "$HELPER" &&
478 check fill <<-\EOF
479 protocol=https
480 host=example.com
481 path=repo.git
482 --
483 protocol=https
484 host=example.com
485 username=foo
486 password=bar
487 --
488 verbatim: get
489 verbatim: protocol=https
490 verbatim: host=example.com
491 EOF
492 '
493
494 test_expect_success 'match percent-encoded values' '
495 test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
496 check fill <<-\EOF
497 url=https://example.com/%2566.git
498 --
499 protocol=https
500 host=example.com
501 username=foo
502 password=bar
503 --
504 EOF
505 '
506
507 test_expect_success 'match percent-encoded UTF-8 values in path' '
508 test_config credential.https://example.com.useHttpPath true &&
509 test_config credential.https://example.com/perú.git.helper "$HELPER" &&
510 check fill <<-\EOF
511 url=https://example.com/per%C3%BA.git
512 --
513 protocol=https
514 host=example.com
515 path=perú.git
516 username=foo
517 password=bar
518 --
519 EOF
520 '
521
522 test_expect_success 'match percent-encoded values in username' '
523 test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
524 check fill <<-\EOF
525 url=https://user%2fname@example.com/foo/bar.git
526 --
527 protocol=https
528 host=example.com
529 username=foo
530 password=bar
531 --
532 EOF
533 '
534
535 test_expect_success 'fetch with multiple path components' '
536 test_unconfig credential.helper &&
537 test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&
538 check fill <<-\EOF
539 url=https://example.com/foo/repo.git
540 --
541 protocol=https
542 host=example.com
543 username=foo
544 password=bar
545 --
546 verbatim: get
547 verbatim: protocol=https
548 verbatim: host=example.com
549 EOF
550 '
551
552 test_expect_success 'pull username from config' '
553 test_config credential.https://example.com.username foo &&
554 check fill <<-\EOF
555 protocol=https
556 host=example.com
557 --
558 protocol=https
559 host=example.com
560 username=foo
561 password=askpass-password
562 --
563 askpass: Password for '\''https://foo@example.com'\'':
564 EOF
565 '
566
567 test_expect_success 'honors username from URL over helper (URL)' '
568 test_config credential.https://example.com.username bob &&
569 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
570 check fill <<-\EOF
571 url=https://alice@example.com
572 --
573 protocol=https
574 host=example.com
575 username=alice
576 password=bar
577 --
578 verbatim: get
579 verbatim: protocol=https
580 verbatim: host=example.com
581 verbatim: username=alice
582 EOF
583 '
584
585 test_expect_success 'honors username from URL over helper (components)' '
586 test_config credential.https://example.com.username bob &&
587 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
588 check fill <<-\EOF
589 protocol=https
590 host=example.com
591 username=alice
592 --
593 protocol=https
594 host=example.com
595 username=alice
596 password=bar
597 --
598 verbatim: get
599 verbatim: protocol=https
600 verbatim: host=example.com
601 verbatim: username=alice
602 EOF
603 '
604
605 test_expect_success 'last matching username wins' '
606 test_config credential.https://example.com/path.git.username bob &&
607 test_config credential.https://example.com.username alice &&
608 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
609 check fill <<-\EOF
610 url=https://example.com/path.git
611 --
612 protocol=https
613 host=example.com
614 username=alice
615 password=bar
616 --
617 verbatim: get
618 verbatim: protocol=https
619 verbatim: host=example.com
620 verbatim: username=alice
621 EOF
622 '
623
624 test_expect_success 'http paths can be part of context' '
625 check fill "verbatim foo bar" <<-\EOF &&
626 protocol=https
627 host=example.com
628 path=foo.git
629 --
630 protocol=https
631 host=example.com
632 username=foo
633 password=bar
634 --
635 verbatim: get
636 verbatim: protocol=https
637 verbatim: host=example.com
638 EOF
639 test_config credential.https://example.com.useHttpPath true &&
640 check fill "verbatim foo bar" <<-\EOF
641 protocol=https
642 host=example.com
643 path=foo.git
644 --
645 protocol=https
646 host=example.com
647 path=foo.git
648 username=foo
649 password=bar
650 --
651 verbatim: get
652 verbatim: protocol=https
653 verbatim: host=example.com
654 verbatim: path=foo.git
655 EOF
656 '
657
658 test_expect_success 'context uses urlmatch' '
659 test_config "credential.https://*.org.useHttpPath" true &&
660 check fill "verbatim foo bar" <<-\EOF
661 protocol=https
662 host=example.org
663 path=foo.git
664 --
665 protocol=https
666 host=example.org
667 path=foo.git
668 username=foo
669 password=bar
670 --
671 verbatim: get
672 verbatim: protocol=https
673 verbatim: host=example.org
674 verbatim: path=foo.git
675 EOF
676 '
677
678 test_expect_success 'helpers can abort the process' '
679 test_must_fail git \
680 -c credential.helper=quit \
681 -c credential.helper="verbatim foo bar" \
682 credential fill >stdout 2>stderr <<-\EOF &&
683 protocol=http
684 host=example.com
685 EOF
686 test_must_be_empty stdout &&
687 cat >expect <<-\EOF &&
688 quit: get
689 quit: protocol=http
690 quit: host=example.com
691 fatal: credential helper '\''quit'\'' told us to quit
692 EOF
693 test_cmp expect stderr
694 '
695
696 test_expect_success 'empty helper spec resets helper list' '
697 test_config credential.helper "verbatim file file" &&
698 check fill "" "verbatim cmdline cmdline" <<-\EOF
699 protocol=http
700 host=example.com
701 --
702 protocol=http
703 host=example.com
704 username=cmdline
705 password=cmdline
706 --
707 verbatim: get
708 verbatim: protocol=http
709 verbatim: host=example.com
710 EOF
711 '
712
713 test_expect_success 'url parser rejects embedded newlines' '
714 test_must_fail git credential fill 2>stderr <<-\EOF &&
715 url=https://one.example.com?%0ahost=two.example.com/
716 EOF
717 cat >expect <<-\EOF &&
718 warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
719 fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
720 EOF
721 test_cmp expect stderr
722 '
723
724 test_expect_success 'host-less URLs are parsed as empty host' '
725 check fill "verbatim foo bar" <<-\EOF
726 url=cert:///path/to/cert.pem
727 --
728 protocol=cert
729 host=
730 path=path/to/cert.pem
731 username=foo
732 password=bar
733 --
734 verbatim: get
735 verbatim: protocol=cert
736 verbatim: host=
737 verbatim: path=path/to/cert.pem
738 EOF
739 '
740
741 test_expect_success 'credential system refuses to work with missing host' '
742 test_must_fail git credential fill 2>stderr <<-\EOF &&
743 protocol=http
744 EOF
745 cat >expect <<-\EOF &&
746 fatal: refusing to work with credential missing host field
747 EOF
748 test_cmp expect stderr
749 '
750
751 test_expect_success 'credential system refuses to work with missing protocol' '
752 test_must_fail git credential fill 2>stderr <<-\EOF &&
753 host=example.com
754 EOF
755 cat >expect <<-\EOF &&
756 fatal: refusing to work with credential missing protocol field
757 EOF
758 test_cmp expect stderr
759 '
760
761 # usage: check_host_and_path <url> <expected-host> <expected-path>
762 check_host_and_path () {
763 # we always parse the path component, but we need this to make sure it
764 # is passed to the helper
765 test_config credential.useHTTPPath true &&
766 check fill "verbatim user pass" <<-EOF
767 url=$1
768 --
769 protocol=https
770 host=$2
771 path=$3
772 username=user
773 password=pass
774 --
775 verbatim: get
776 verbatim: protocol=https
777 verbatim: host=$2
778 verbatim: path=$3
779 EOF
780 }
781
782 test_expect_success 'url parser handles bare query marker' '
783 check_host_and_path https://example.com?foo.git example.com ?foo.git
784 '
785
786 test_expect_success 'url parser handles bare fragment marker' '
787 check_host_and_path https://example.com#foo.git example.com "#foo.git"
788 '
789
790 test_expect_success 'url parser not confused by encoded markers' '
791 check_host_and_path https://example.com%23%3f%2f/foo.git \
792 "example.com#?/" foo.git
793 '
794
795 test_expect_success 'credential config with partial URLs' '
796 echo "echo password=yep" | write_script git-credential-yep &&
797 test_write_lines url=https://user@example.com/repo.git >stdin &&
798 for partial in \
799 example.com \
800 user@example.com \
801 https:// \
802 https://example.com \
803 https://example.com/ \
804 https://user@example.com \
805 https://user@example.com/ \
806 https://example.com/repo.git \
807 https://user@example.com/repo.git \
808 /repo.git
809 do
810 git -c credential.$partial.helper=yep \
811 credential fill <stdin >stdout &&
812 grep yep stdout ||
813 return 1
814 done &&
815
816 for partial in \
817 dont.use.this \
818 http:// \
819 /repo
820 do
821 git -c credential.$partial.helper=yep \
822 credential fill <stdin >stdout &&
823 ! grep yep stdout ||
824 return 1
825 done &&
826
827 git -c credential.$partial.helper=yep \
828 -c credential.with%0anewline.username=uh-oh \
829 credential fill <stdin 2>stderr &&
830 test_i18ngrep "skipping credential lookup for key" stderr
831 '
832
833 test_done