Git 2.46-rc1
[alt-git.git] / t / t0300-credentials.sh
blob6a76b7fdbd4557b2df8e34f9683323cf8073ea38
1 #!/bin/sh
3 test_description='basic credential helper tests'
5 TEST_PASSES_SANITIZE_LEAK=true
6 . ./test-lib.sh
7 . "$TEST_DIRECTORY"/lib-credential.sh
9 test_expect_success 'setup helper scripts' '
10 cat >dump <<-\EOF &&
11 whoami=$(echo $0 | sed s/.*git-credential-//)
12 echo >&2 "$whoami: $*"
13 OIFS=$IFS
14 IFS==
15 while read key value; do
16 echo >&2 "$whoami: $key=$value"
17 if test -z "${key%%*\[\]}"
18 then
19 key=${key%%\[\]}
20 eval "$key=\"\$$key $value\""
21 else
22 eval "$key=$value"
24 done
25 IFS=$OIFS
26 EOF
28 write_script git-credential-useless <<-\EOF &&
29 . ./dump
30 exit 0
31 EOF
33 write_script git-credential-quit <<-\EOF &&
34 . ./dump
35 echo quit=1
36 EOF
38 write_script git-credential-verbatim <<-\EOF &&
39 user=$1; shift
40 pass=$1; shift
41 . ./dump
42 test -z "$user" || echo username=$user
43 test -z "$pass" || echo password=$pass
44 EOF
46 write_script git-credential-verbatim-cred <<-\EOF &&
47 authtype=$1; shift
48 credential=$1; shift
49 . ./dump
50 echo capability[]=authtype
51 echo capability[]=state
52 test -z "${capability##*authtype*}" || exit 0
53 test -z "$authtype" || echo authtype=$authtype
54 test -z "$credential" || echo credential=$credential
55 test -z "${capability##*state*}" || exit 0
56 echo state[]=verbatim-cred:foo
57 EOF
59 write_script git-credential-verbatim-ephemeral <<-\EOF &&
60 authtype=$1; shift
61 credential=$1; shift
62 . ./dump
63 echo capability[]=authtype
64 test -z "${capability##*authtype*}" || exit 0
65 test -z "$authtype" || echo authtype=$authtype
66 test -z "$credential" || echo credential=$credential
67 echo "ephemeral=1"
68 EOF
70 write_script git-credential-verbatim-with-expiry <<-\EOF &&
71 user=$1; shift
72 pass=$1; shift
73 pexpiry=$1; shift
74 . ./dump
75 test -z "$user" || echo username=$user
76 test -z "$pass" || echo password=$pass
77 test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
78 EOF
80 PATH="$PWD:$PATH"
83 test_expect_success 'credential_fill invokes helper' '
84 check fill "verbatim foo bar" <<-\EOF
85 protocol=http
86 host=example.com
88 protocol=http
89 host=example.com
90 username=foo
91 password=bar
93 verbatim: get
94 verbatim: protocol=http
95 verbatim: host=example.com
96 EOF
99 test_expect_success 'credential_fill invokes helper with credential' '
100 check fill "verbatim-cred Bearer token" <<-\EOF
101 capability[]=authtype
102 protocol=http
103 host=example.com
105 capability[]=authtype
106 authtype=Bearer
107 credential=token
108 protocol=http
109 host=example.com
111 verbatim-cred: get
112 verbatim-cred: capability[]=authtype
113 verbatim-cred: protocol=http
114 verbatim-cred: host=example.com
118 test_expect_success 'credential_fill invokes helper with ephemeral credential' '
119 check fill "verbatim-ephemeral Bearer token" <<-\EOF
120 capability[]=authtype
121 protocol=http
122 host=example.com
124 capability[]=authtype
125 authtype=Bearer
126 credential=token
127 ephemeral=1
128 protocol=http
129 host=example.com
131 verbatim-ephemeral: get
132 verbatim-ephemeral: capability[]=authtype
133 verbatim-ephemeral: protocol=http
134 verbatim-ephemeral: host=example.com
137 test_expect_success 'credential_fill invokes helper with credential and state' '
138 check fill "verbatim-cred Bearer token" <<-\EOF
139 capability[]=authtype
140 capability[]=state
141 protocol=http
142 host=example.com
144 capability[]=authtype
145 capability[]=state
146 authtype=Bearer
147 credential=token
148 protocol=http
149 host=example.com
150 state[]=verbatim-cred:foo
152 verbatim-cred: get
153 verbatim-cred: capability[]=authtype
154 verbatim-cred: capability[]=state
155 verbatim-cred: protocol=http
156 verbatim-cred: host=example.com
160 test_expect_success 'credential_fill invokes multiple helpers' '
161 check fill useless "verbatim foo bar" <<-\EOF
162 protocol=http
163 host=example.com
165 protocol=http
166 host=example.com
167 username=foo
168 password=bar
170 useless: get
171 useless: protocol=http
172 useless: host=example.com
173 verbatim: get
174 verbatim: protocol=http
175 verbatim: host=example.com
179 test_expect_success 'credential_fill response does not get capabilities when helpers are incapable' '
180 check fill useless "verbatim foo bar" <<-\EOF
181 capability[]=authtype
182 capability[]=state
183 protocol=http
184 host=example.com
186 protocol=http
187 host=example.com
188 username=foo
189 password=bar
191 useless: get
192 useless: capability[]=authtype
193 useless: capability[]=state
194 useless: protocol=http
195 useless: host=example.com
196 verbatim: get
197 verbatim: capability[]=authtype
198 verbatim: capability[]=state
199 verbatim: protocol=http
200 verbatim: host=example.com
204 test_expect_success 'credential_fill response does not get capabilities when caller is incapable' '
205 check fill "verbatim-cred Bearer token" <<-\EOF
206 protocol=http
207 host=example.com
209 protocol=http
210 host=example.com
212 verbatim-cred: get
213 verbatim-cred: protocol=http
214 verbatim-cred: host=example.com
218 test_expect_success 'credential_fill stops when we get a full response' '
219 check fill "verbatim one two" "verbatim three four" <<-\EOF
220 protocol=http
221 host=example.com
223 protocol=http
224 host=example.com
225 username=one
226 password=two
228 verbatim: get
229 verbatim: protocol=http
230 verbatim: host=example.com
234 test_expect_success 'credential_fill thinks a credential is a full response' '
235 check fill "verbatim-cred Bearer token" "verbatim three four" <<-\EOF
236 capability[]=authtype
237 protocol=http
238 host=example.com
240 capability[]=authtype
241 authtype=Bearer
242 credential=token
243 protocol=http
244 host=example.com
246 verbatim-cred: get
247 verbatim-cred: capability[]=authtype
248 verbatim-cred: protocol=http
249 verbatim-cred: host=example.com
253 test_expect_success 'credential_fill continues through partial response' '
254 check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
255 protocol=http
256 host=example.com
258 protocol=http
259 host=example.com
260 username=two
261 password=three
263 verbatim: get
264 verbatim: protocol=http
265 verbatim: host=example.com
266 verbatim: get
267 verbatim: protocol=http
268 verbatim: host=example.com
269 verbatim: username=one
273 test_expect_success 'credential_fill populates password_expiry_utc' '
274 check fill "verbatim-with-expiry one two 9999999999" <<-\EOF
275 protocol=http
276 host=example.com
278 protocol=http
279 host=example.com
280 username=one
281 password=two
282 password_expiry_utc=9999999999
284 verbatim-with-expiry: get
285 verbatim-with-expiry: protocol=http
286 verbatim-with-expiry: host=example.com
290 test_expect_success 'credential_fill ignores expired password' '
291 check fill "verbatim-with-expiry one two 5" "verbatim three four" <<-\EOF
292 protocol=http
293 host=example.com
295 protocol=http
296 host=example.com
297 username=three
298 password=four
300 verbatim-with-expiry: get
301 verbatim-with-expiry: protocol=http
302 verbatim-with-expiry: host=example.com
303 verbatim: get
304 verbatim: protocol=http
305 verbatim: host=example.com
306 verbatim: username=one
310 test_expect_success 'credential_fill passes along metadata' '
311 check fill "verbatim one two" <<-\EOF
312 protocol=ftp
313 host=example.com
314 path=foo.git
316 protocol=ftp
317 host=example.com
318 path=foo.git
319 username=one
320 password=two
322 verbatim: get
323 verbatim: protocol=ftp
324 verbatim: host=example.com
325 verbatim: path=foo.git
329 test_expect_success 'credential_fill produces no credential without capability' '
330 check fill "verbatim-cred Bearer token" <<-\EOF
331 protocol=http
332 host=example.com
334 protocol=http
335 host=example.com
337 verbatim-cred: get
338 verbatim-cred: protocol=http
339 verbatim-cred: host=example.com
343 test_expect_success 'credential_approve calls all helpers' '
344 check approve useless "verbatim one two" <<-\EOF
345 protocol=http
346 host=example.com
347 username=foo
348 password=bar
351 useless: store
352 useless: protocol=http
353 useless: host=example.com
354 useless: username=foo
355 useless: password=bar
356 verbatim: store
357 verbatim: protocol=http
358 verbatim: host=example.com
359 verbatim: username=foo
360 verbatim: password=bar
364 test_expect_success 'credential_approve stores password expiry' '
365 check approve useless <<-\EOF
366 protocol=http
367 host=example.com
368 username=foo
369 password=bar
370 password_expiry_utc=9999999999
373 useless: store
374 useless: protocol=http
375 useless: host=example.com
376 useless: username=foo
377 useless: password=bar
378 useless: password_expiry_utc=9999999999
382 test_expect_success 'credential_approve stores oauth refresh token' '
383 check approve useless <<-\EOF
384 protocol=http
385 host=example.com
386 username=foo
387 password=bar
388 oauth_refresh_token=xyzzy
391 useless: store
392 useless: protocol=http
393 useless: host=example.com
394 useless: username=foo
395 useless: password=bar
396 useless: oauth_refresh_token=xyzzy
400 test_expect_success 'do not bother storing password-less credential' '
401 check approve useless <<-\EOF
402 protocol=http
403 host=example.com
404 username=foo
410 test_expect_success 'credential_approve does not store expired password' '
411 check approve useless <<-\EOF
412 protocol=http
413 host=example.com
414 username=foo
415 password=bar
416 password_expiry_utc=5
422 test_expect_success 'credential_reject calls all helpers' '
423 check reject useless "verbatim one two" <<-\EOF
424 protocol=http
425 host=example.com
426 username=foo
427 password=bar
430 useless: erase
431 useless: protocol=http
432 useless: host=example.com
433 useless: username=foo
434 useless: password=bar
435 verbatim: erase
436 verbatim: protocol=http
437 verbatim: host=example.com
438 verbatim: username=foo
439 verbatim: password=bar
443 test_expect_success 'credential_reject erases credential regardless of expiry' '
444 check reject useless <<-\EOF
445 protocol=http
446 host=example.com
447 username=foo
448 password=bar
449 password_expiry_utc=5
452 useless: erase
453 useless: protocol=http
454 useless: host=example.com
455 useless: username=foo
456 useless: password=bar
457 useless: password_expiry_utc=5
461 test_expect_success 'usernames can be preserved' '
462 check fill "verbatim \"\" three" <<-\EOF
463 protocol=http
464 host=example.com
465 username=one
467 protocol=http
468 host=example.com
469 username=one
470 password=three
472 verbatim: get
473 verbatim: protocol=http
474 verbatim: host=example.com
475 verbatim: username=one
479 test_expect_success 'usernames can be overridden' '
480 check fill "verbatim two three" <<-\EOF
481 protocol=http
482 host=example.com
483 username=one
485 protocol=http
486 host=example.com
487 username=two
488 password=three
490 verbatim: get
491 verbatim: protocol=http
492 verbatim: host=example.com
493 verbatim: username=one
497 test_expect_success 'do not bother completing already-full credential' '
498 check fill "verbatim three four" <<-\EOF
499 protocol=http
500 host=example.com
501 username=one
502 password=two
504 protocol=http
505 host=example.com
506 username=one
507 password=two
512 # We can't test the basic terminal password prompt here because
513 # getpass() tries too hard to find the real terminal. But if our
514 # askpass helper is run, we know the internal getpass is working.
515 test_expect_success 'empty helper list falls back to internal getpass' '
516 check fill <<-\EOF
517 protocol=http
518 host=example.com
520 protocol=http
521 host=example.com
522 username=askpass-username
523 password=askpass-password
525 askpass: Username for '\''http://example.com'\'':
526 askpass: Password for '\''http://askpass-username@example.com'\'':
530 test_expect_success 'internal getpass does not ask for known username' '
531 check fill <<-\EOF
532 protocol=http
533 host=example.com
534 username=foo
536 protocol=http
537 host=example.com
538 username=foo
539 password=askpass-password
541 askpass: Password for '\''http://foo@example.com'\'':
545 test_expect_success 'git-credential respects core.askPass' '
546 write_script alternate-askpass <<-\EOF &&
547 echo >&2 "alternate askpass invoked"
548 echo alternate-value
550 test_config core.askpass "$PWD/alternate-askpass" &&
552 # unset GIT_ASKPASS set by lib-credential.sh which would
553 # override our config, but do so in a subshell so that we do
554 # not interfere with other tests
555 sane_unset GIT_ASKPASS &&
556 check fill <<-\EOF
557 protocol=http
558 host=example.com
560 protocol=http
561 host=example.com
562 username=alternate-value
563 password=alternate-value
565 alternate askpass invoked
566 alternate askpass invoked
571 HELPER="!f() {
572 cat >/dev/null
573 echo username=foo
574 echo password=bar
575 }; f"
576 test_expect_success 'respect configured credentials' '
577 test_config credential.helper "$HELPER" &&
578 check fill <<-\EOF
579 protocol=http
580 host=example.com
582 protocol=http
583 host=example.com
584 username=foo
585 password=bar
590 test_expect_success 'match configured credential' '
591 test_config credential.https://example.com.helper "$HELPER" &&
592 check fill <<-\EOF
593 protocol=https
594 host=example.com
595 path=repo.git
597 protocol=https
598 host=example.com
599 username=foo
600 password=bar
605 test_expect_success 'do not match configured credential' '
606 test_config credential.https://foo.helper "$HELPER" &&
607 check fill <<-\EOF
608 protocol=https
609 host=bar
611 protocol=https
612 host=bar
613 username=askpass-username
614 password=askpass-password
616 askpass: Username for '\''https://bar'\'':
617 askpass: Password for '\''https://askpass-username@bar'\'':
621 test_expect_success 'match multiple configured helpers' '
622 test_config credential.helper "verbatim \"\" \"\"" &&
623 test_config credential.https://example.com.helper "$HELPER" &&
624 check fill <<-\EOF
625 protocol=https
626 host=example.com
627 path=repo.git
629 protocol=https
630 host=example.com
631 username=foo
632 password=bar
634 verbatim: get
635 verbatim: protocol=https
636 verbatim: host=example.com
640 test_expect_success 'match multiple configured helpers with URLs' '
641 test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
642 test_config credential.https://example.com.helper "$HELPER" &&
643 check fill <<-\EOF
644 protocol=https
645 host=example.com
646 path=repo.git
648 protocol=https
649 host=example.com
650 username=foo
651 password=bar
653 verbatim: get
654 verbatim: protocol=https
655 verbatim: host=example.com
659 test_expect_success 'match percent-encoded values' '
660 test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
661 check fill <<-\EOF
662 url=https://example.com/%2566.git
664 protocol=https
665 host=example.com
666 username=foo
667 password=bar
672 test_expect_success 'match percent-encoded UTF-8 values in path' '
673 test_config credential.https://example.com.useHttpPath true &&
674 test_config credential.https://example.com/perĂº.git.helper "$HELPER" &&
675 check fill <<-\EOF
676 url=https://example.com/per%C3%BA.git
678 protocol=https
679 host=example.com
680 path=perĂº.git
681 username=foo
682 password=bar
687 test_expect_success 'match percent-encoded values in username' '
688 test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
689 check fill <<-\EOF
690 url=https://user%2fname@example.com/foo/bar.git
692 protocol=https
693 host=example.com
694 username=foo
695 password=bar
700 test_expect_success 'fetch with multiple path components' '
701 test_unconfig credential.helper &&
702 test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&
703 check fill <<-\EOF
704 url=https://example.com/foo/repo.git
706 protocol=https
707 host=example.com
708 username=foo
709 password=bar
711 verbatim: get
712 verbatim: protocol=https
713 verbatim: host=example.com
717 test_expect_success 'pull username from config' '
718 test_config credential.https://example.com.username foo &&
719 check fill <<-\EOF
720 protocol=https
721 host=example.com
723 protocol=https
724 host=example.com
725 username=foo
726 password=askpass-password
728 askpass: Password for '\''https://foo@example.com'\'':
732 test_expect_success 'honors username from URL over helper (URL)' '
733 test_config credential.https://example.com.username bob &&
734 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
735 check fill <<-\EOF
736 url=https://alice@example.com
738 protocol=https
739 host=example.com
740 username=alice
741 password=bar
743 verbatim: get
744 verbatim: protocol=https
745 verbatim: host=example.com
746 verbatim: username=alice
750 test_expect_success 'honors username from URL over helper (components)' '
751 test_config credential.https://example.com.username bob &&
752 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
753 check fill <<-\EOF
754 protocol=https
755 host=example.com
756 username=alice
758 protocol=https
759 host=example.com
760 username=alice
761 password=bar
763 verbatim: get
764 verbatim: protocol=https
765 verbatim: host=example.com
766 verbatim: username=alice
770 test_expect_success 'last matching username wins' '
771 test_config credential.https://example.com/path.git.username bob &&
772 test_config credential.https://example.com.username alice &&
773 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
774 check fill <<-\EOF
775 url=https://example.com/path.git
777 protocol=https
778 host=example.com
779 username=alice
780 password=bar
782 verbatim: get
783 verbatim: protocol=https
784 verbatim: host=example.com
785 verbatim: username=alice
789 test_expect_success 'http paths can be part of context' '
790 check fill "verbatim foo bar" <<-\EOF &&
791 protocol=https
792 host=example.com
793 path=foo.git
795 protocol=https
796 host=example.com
797 username=foo
798 password=bar
800 verbatim: get
801 verbatim: protocol=https
802 verbatim: host=example.com
804 test_config credential.https://example.com.useHttpPath true &&
805 check fill "verbatim foo bar" <<-\EOF
806 protocol=https
807 host=example.com
808 path=foo.git
810 protocol=https
811 host=example.com
812 path=foo.git
813 username=foo
814 password=bar
816 verbatim: get
817 verbatim: protocol=https
818 verbatim: host=example.com
819 verbatim: path=foo.git
823 test_expect_success 'context uses urlmatch' '
824 test_config "credential.https://*.org.useHttpPath" true &&
825 check fill "verbatim foo bar" <<-\EOF
826 protocol=https
827 host=example.org
828 path=foo.git
830 protocol=https
831 host=example.org
832 path=foo.git
833 username=foo
834 password=bar
836 verbatim: get
837 verbatim: protocol=https
838 verbatim: host=example.org
839 verbatim: path=foo.git
843 test_expect_success 'helpers can abort the process' '
844 test_must_fail git \
845 -c credential.helper=quit \
846 -c credential.helper="verbatim foo bar" \
847 credential fill >stdout 2>stderr <<-\EOF &&
848 protocol=http
849 host=example.com
851 test_must_be_empty stdout &&
852 cat >expect <<-\EOF &&
853 quit: get
854 quit: protocol=http
855 quit: host=example.com
856 fatal: credential helper '\''quit'\'' told us to quit
858 test_cmp expect stderr
861 test_expect_success 'empty helper spec resets helper list' '
862 test_config credential.helper "verbatim file file" &&
863 check fill "" "verbatim cmdline cmdline" <<-\EOF
864 protocol=http
865 host=example.com
867 protocol=http
868 host=example.com
869 username=cmdline
870 password=cmdline
872 verbatim: get
873 verbatim: protocol=http
874 verbatim: host=example.com
878 test_expect_success 'url parser rejects embedded newlines' '
879 test_must_fail git credential fill 2>stderr <<-\EOF &&
880 url=https://one.example.com?%0ahost=two.example.com/
882 cat >expect <<-\EOF &&
883 warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
884 fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
886 test_cmp expect stderr
889 test_expect_success 'host-less URLs are parsed as empty host' '
890 check fill "verbatim foo bar" <<-\EOF
891 url=cert:///path/to/cert.pem
893 protocol=cert
894 host=
895 path=path/to/cert.pem
896 username=foo
897 password=bar
899 verbatim: get
900 verbatim: protocol=cert
901 verbatim: host=
902 verbatim: path=path/to/cert.pem
906 test_expect_success 'credential system refuses to work with missing host' '
907 test_must_fail git credential fill 2>stderr <<-\EOF &&
908 protocol=http
910 cat >expect <<-\EOF &&
911 fatal: refusing to work with credential missing host field
913 test_cmp expect stderr
916 test_expect_success 'credential system refuses to work with missing protocol' '
917 test_must_fail git credential fill 2>stderr <<-\EOF &&
918 host=example.com
920 cat >expect <<-\EOF &&
921 fatal: refusing to work with credential missing protocol field
923 test_cmp expect stderr
926 # usage: check_host_and_path <url> <expected-host> <expected-path>
927 check_host_and_path () {
928 # we always parse the path component, but we need this to make sure it
929 # is passed to the helper
930 test_config credential.useHTTPPath true &&
931 check fill "verbatim user pass" <<-EOF
932 url=$1
934 protocol=https
935 host=$2
936 path=$3
937 username=user
938 password=pass
940 verbatim: get
941 verbatim: protocol=https
942 verbatim: host=$2
943 verbatim: path=$3
947 test_expect_success 'url parser handles bare query marker' '
948 check_host_and_path https://example.com?foo.git example.com ?foo.git
951 test_expect_success 'url parser handles bare fragment marker' '
952 check_host_and_path https://example.com#foo.git example.com "#foo.git"
955 test_expect_success 'url parser not confused by encoded markers' '
956 check_host_and_path https://example.com%23%3f%2f/foo.git \
957 "example.com#?/" foo.git
960 test_expect_success 'credential config with partial URLs' '
961 echo "echo password=yep" | write_script git-credential-yep &&
962 test_write_lines url=https://user@example.com/repo.git >stdin &&
963 for partial in \
964 example.com \
965 user@example.com \
966 https:// \
967 https://example.com \
968 https://example.com/ \
969 https://user@example.com \
970 https://user@example.com/ \
971 https://example.com/repo.git \
972 https://user@example.com/repo.git \
973 /repo.git
975 git -c credential.$partial.helper=yep \
976 credential fill <stdin >stdout &&
977 grep yep stdout ||
978 return 1
979 done &&
981 for partial in \
982 dont.use.this \
983 http:// \
984 /repo
986 git -c credential.$partial.helper=yep \
987 credential fill <stdin >stdout &&
988 ! grep yep stdout ||
989 return 1
990 done &&
992 git -c credential.$partial.helper=yep \
993 -c credential.with%0anewline.username=uh-oh \
994 credential fill <stdin 2>stderr &&
995 test_grep "skipping credential lookup for key" stderr
998 test_done