The third batch
[git.git] / t / t0300-credentials.sh
blob432f029d4882dff2ce44fcbed9e99145254cbcdf
1 #!/bin/sh
3 test_description='basic credential helper tests'
4 . ./test-lib.sh
5 . "$TEST_DIRECTORY"/lib-credential.sh
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 if test -z "${key%%*\[\]}"
16 then
17 key=${key%%\[\]}
18 eval "$key=\"\$$key $value\""
19 else
20 eval "$key=$value"
22 done
23 IFS=$OIFS
24 EOF
26 write_script git-credential-useless <<-\EOF &&
27 . ./dump
28 exit 0
29 EOF
31 write_script git-credential-quit <<-\EOF &&
32 . ./dump
33 echo quit=1
34 EOF
36 write_script git-credential-verbatim <<-\EOF &&
37 user=$1; shift
38 pass=$1; shift
39 . ./dump
40 test -z "$user" || echo username=$user
41 test -z "$pass" || echo password=$pass
42 EOF
44 write_script git-credential-verbatim-cred <<-\EOF &&
45 authtype=$1; shift
46 credential=$1; shift
47 . ./dump
48 echo capability[]=authtype
49 echo capability[]=state
50 test -z "${capability##*authtype*}" || exit 0
51 test -z "$authtype" || echo authtype=$authtype
52 test -z "$credential" || echo credential=$credential
53 test -z "${capability##*state*}" || exit 0
54 echo state[]=verbatim-cred:foo
55 EOF
57 write_script git-credential-verbatim-ephemeral <<-\EOF &&
58 authtype=$1; shift
59 credential=$1; shift
60 . ./dump
61 echo capability[]=authtype
62 test -z "${capability##*authtype*}" || exit 0
63 test -z "$authtype" || echo authtype=$authtype
64 test -z "$credential" || echo credential=$credential
65 echo "ephemeral=1"
66 EOF
68 write_script git-credential-verbatim-with-expiry <<-\EOF &&
69 user=$1; shift
70 pass=$1; shift
71 pexpiry=$1; shift
72 . ./dump
73 test -z "$user" || echo username=$user
74 test -z "$pass" || echo password=$pass
75 test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
76 EOF
78 PATH="$PWD:$PATH"
81 test_expect_success 'credential_fill invokes helper' '
82 check fill "verbatim foo bar" <<-\EOF
83 protocol=http
84 host=example.com
86 protocol=http
87 host=example.com
88 username=foo
89 password=bar
91 verbatim: get
92 verbatim: protocol=http
93 verbatim: host=example.com
94 EOF
97 test_expect_success 'credential_fill invokes helper with credential' '
98 check fill "verbatim-cred Bearer token" <<-\EOF
99 capability[]=authtype
100 protocol=http
101 host=example.com
103 capability[]=authtype
104 authtype=Bearer
105 credential=token
106 protocol=http
107 host=example.com
109 verbatim-cred: get
110 verbatim-cred: capability[]=authtype
111 verbatim-cred: protocol=http
112 verbatim-cred: host=example.com
116 test_expect_success 'credential_fill invokes helper with ephemeral credential' '
117 check fill "verbatim-ephemeral Bearer token" <<-\EOF
118 capability[]=authtype
119 protocol=http
120 host=example.com
122 capability[]=authtype
123 authtype=Bearer
124 credential=token
125 ephemeral=1
126 protocol=http
127 host=example.com
129 verbatim-ephemeral: get
130 verbatim-ephemeral: capability[]=authtype
131 verbatim-ephemeral: protocol=http
132 verbatim-ephemeral: host=example.com
135 test_expect_success 'credential_fill invokes helper with credential and state' '
136 check fill "verbatim-cred Bearer token" <<-\EOF
137 capability[]=authtype
138 capability[]=state
139 protocol=http
140 host=example.com
142 capability[]=authtype
143 capability[]=state
144 authtype=Bearer
145 credential=token
146 protocol=http
147 host=example.com
148 state[]=verbatim-cred:foo
150 verbatim-cred: get
151 verbatim-cred: capability[]=authtype
152 verbatim-cred: capability[]=state
153 verbatim-cred: protocol=http
154 verbatim-cred: host=example.com
158 test_expect_success 'credential_fill invokes multiple helpers' '
159 check fill useless "verbatim foo bar" <<-\EOF
160 protocol=http
161 host=example.com
163 protocol=http
164 host=example.com
165 username=foo
166 password=bar
168 useless: get
169 useless: protocol=http
170 useless: host=example.com
171 verbatim: get
172 verbatim: protocol=http
173 verbatim: host=example.com
177 test_expect_success 'credential_fill response does not get capabilities when helpers are incapable' '
178 check fill useless "verbatim foo bar" <<-\EOF
179 capability[]=authtype
180 capability[]=state
181 protocol=http
182 host=example.com
184 protocol=http
185 host=example.com
186 username=foo
187 password=bar
189 useless: get
190 useless: capability[]=authtype
191 useless: capability[]=state
192 useless: protocol=http
193 useless: host=example.com
194 verbatim: get
195 verbatim: capability[]=authtype
196 verbatim: capability[]=state
197 verbatim: protocol=http
198 verbatim: host=example.com
202 test_expect_success 'credential_fill response does not get capabilities when caller is incapable' '
203 check fill "verbatim-cred Bearer token" <<-\EOF
204 protocol=http
205 host=example.com
207 protocol=http
208 host=example.com
210 verbatim-cred: get
211 verbatim-cred: protocol=http
212 verbatim-cred: host=example.com
216 test_expect_success 'credential_fill stops when we get a full response' '
217 check fill "verbatim one two" "verbatim three four" <<-\EOF
218 protocol=http
219 host=example.com
221 protocol=http
222 host=example.com
223 username=one
224 password=two
226 verbatim: get
227 verbatim: protocol=http
228 verbatim: host=example.com
232 test_expect_success 'credential_fill thinks a credential is a full response' '
233 check fill "verbatim-cred Bearer token" "verbatim three four" <<-\EOF
234 capability[]=authtype
235 protocol=http
236 host=example.com
238 capability[]=authtype
239 authtype=Bearer
240 credential=token
241 protocol=http
242 host=example.com
244 verbatim-cred: get
245 verbatim-cred: capability[]=authtype
246 verbatim-cred: protocol=http
247 verbatim-cred: host=example.com
251 test_expect_success 'credential_fill continues through partial response' '
252 check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
253 protocol=http
254 host=example.com
256 protocol=http
257 host=example.com
258 username=two
259 password=three
261 verbatim: get
262 verbatim: protocol=http
263 verbatim: host=example.com
264 verbatim: get
265 verbatim: protocol=http
266 verbatim: host=example.com
267 verbatim: username=one
271 test_expect_success 'credential_fill populates password_expiry_utc' '
272 check fill "verbatim-with-expiry one two 9999999999" <<-\EOF
273 protocol=http
274 host=example.com
276 protocol=http
277 host=example.com
278 username=one
279 password=two
280 password_expiry_utc=9999999999
282 verbatim-with-expiry: get
283 verbatim-with-expiry: protocol=http
284 verbatim-with-expiry: host=example.com
288 test_expect_success 'credential_fill ignores expired password' '
289 check fill "verbatim-with-expiry one two 5" "verbatim three four" <<-\EOF
290 protocol=http
291 host=example.com
293 protocol=http
294 host=example.com
295 username=three
296 password=four
298 verbatim-with-expiry: get
299 verbatim-with-expiry: protocol=http
300 verbatim-with-expiry: host=example.com
301 verbatim: get
302 verbatim: protocol=http
303 verbatim: host=example.com
304 verbatim: username=one
308 test_expect_success 'credential_fill passes along metadata' '
309 check fill "verbatim one two" <<-\EOF
310 protocol=ftp
311 host=example.com
312 path=foo.git
314 protocol=ftp
315 host=example.com
316 path=foo.git
317 username=one
318 password=two
320 verbatim: get
321 verbatim: protocol=ftp
322 verbatim: host=example.com
323 verbatim: path=foo.git
327 test_expect_success 'credential_fill produces no credential without capability' '
328 check fill "verbatim-cred Bearer token" <<-\EOF
329 protocol=http
330 host=example.com
332 protocol=http
333 host=example.com
335 verbatim-cred: get
336 verbatim-cred: protocol=http
337 verbatim-cred: host=example.com
341 test_expect_success 'credential_approve calls all helpers' '
342 check approve useless "verbatim one two" <<-\EOF
343 protocol=http
344 host=example.com
345 username=foo
346 password=bar
349 useless: store
350 useless: protocol=http
351 useless: host=example.com
352 useless: username=foo
353 useless: password=bar
354 verbatim: store
355 verbatim: protocol=http
356 verbatim: host=example.com
357 verbatim: username=foo
358 verbatim: password=bar
362 test_expect_success 'credential_approve stores password expiry' '
363 check approve useless <<-\EOF
364 protocol=http
365 host=example.com
366 username=foo
367 password=bar
368 password_expiry_utc=9999999999
371 useless: store
372 useless: protocol=http
373 useless: host=example.com
374 useless: username=foo
375 useless: password=bar
376 useless: password_expiry_utc=9999999999
380 test_expect_success 'credential_approve stores oauth refresh token' '
381 check approve useless <<-\EOF
382 protocol=http
383 host=example.com
384 username=foo
385 password=bar
386 oauth_refresh_token=xyzzy
389 useless: store
390 useless: protocol=http
391 useless: host=example.com
392 useless: username=foo
393 useless: password=bar
394 useless: oauth_refresh_token=xyzzy
398 test_expect_success 'do not bother storing password-less credential' '
399 check approve useless <<-\EOF
400 protocol=http
401 host=example.com
402 username=foo
408 test_expect_success 'credential_approve does not store expired password' '
409 check approve useless <<-\EOF
410 protocol=http
411 host=example.com
412 username=foo
413 password=bar
414 password_expiry_utc=5
420 test_expect_success 'credential_reject calls all helpers' '
421 check reject useless "verbatim one two" <<-\EOF
422 protocol=http
423 host=example.com
424 username=foo
425 password=bar
428 useless: erase
429 useless: protocol=http
430 useless: host=example.com
431 useless: username=foo
432 useless: password=bar
433 verbatim: erase
434 verbatim: protocol=http
435 verbatim: host=example.com
436 verbatim: username=foo
437 verbatim: password=bar
441 test_expect_success 'credential_reject erases credential regardless of expiry' '
442 check reject useless <<-\EOF
443 protocol=http
444 host=example.com
445 username=foo
446 password=bar
447 password_expiry_utc=5
450 useless: erase
451 useless: protocol=http
452 useless: host=example.com
453 useless: username=foo
454 useless: password=bar
455 useless: password_expiry_utc=5
459 test_expect_success 'usernames can be preserved' '
460 check fill "verbatim \"\" three" <<-\EOF
461 protocol=http
462 host=example.com
463 username=one
465 protocol=http
466 host=example.com
467 username=one
468 password=three
470 verbatim: get
471 verbatim: protocol=http
472 verbatim: host=example.com
473 verbatim: username=one
477 test_expect_success 'usernames can be overridden' '
478 check fill "verbatim two three" <<-\EOF
479 protocol=http
480 host=example.com
481 username=one
483 protocol=http
484 host=example.com
485 username=two
486 password=three
488 verbatim: get
489 verbatim: protocol=http
490 verbatim: host=example.com
491 verbatim: username=one
495 test_expect_success 'do not bother completing already-full credential' '
496 check fill "verbatim three four" <<-\EOF
497 protocol=http
498 host=example.com
499 username=one
500 password=two
502 protocol=http
503 host=example.com
504 username=one
505 password=two
510 # We can't test the basic terminal password prompt here because
511 # getpass() tries too hard to find the real terminal. But if our
512 # askpass helper is run, we know the internal getpass is working.
513 test_expect_success 'empty helper list falls back to internal getpass' '
514 check fill <<-\EOF
515 protocol=http
516 host=example.com
518 protocol=http
519 host=example.com
520 username=askpass-username
521 password=askpass-password
523 askpass: Username for '\''http://example.com'\'':
524 askpass: Password for '\''http://askpass-username@example.com'\'':
528 test_expect_success 'internal getpass does not ask for known username' '
529 check fill <<-\EOF
530 protocol=http
531 host=example.com
532 username=foo
534 protocol=http
535 host=example.com
536 username=foo
537 password=askpass-password
539 askpass: Password for '\''http://foo@example.com'\'':
543 test_expect_success 'git-credential respects core.askPass' '
544 write_script alternate-askpass <<-\EOF &&
545 echo >&2 "alternate askpass invoked"
546 echo alternate-value
548 test_config core.askpass "$PWD/alternate-askpass" &&
550 # unset GIT_ASKPASS set by lib-credential.sh which would
551 # override our config, but do so in a subshell so that we do
552 # not interfere with other tests
553 sane_unset GIT_ASKPASS &&
554 check fill <<-\EOF
555 protocol=http
556 host=example.com
558 protocol=http
559 host=example.com
560 username=alternate-value
561 password=alternate-value
563 alternate askpass invoked
564 alternate askpass invoked
569 HELPER="!f() {
570 cat >/dev/null
571 echo username=foo
572 echo password=bar
573 }; f"
574 test_expect_success 'respect configured credentials' '
575 test_config credential.helper "$HELPER" &&
576 check fill <<-\EOF
577 protocol=http
578 host=example.com
580 protocol=http
581 host=example.com
582 username=foo
583 password=bar
588 test_expect_success 'match configured credential' '
589 test_config credential.https://example.com.helper "$HELPER" &&
590 check fill <<-\EOF
591 protocol=https
592 host=example.com
593 path=repo.git
595 protocol=https
596 host=example.com
597 username=foo
598 password=bar
603 test_expect_success 'do not match configured credential' '
604 test_config credential.https://foo.helper "$HELPER" &&
605 check fill <<-\EOF
606 protocol=https
607 host=bar
609 protocol=https
610 host=bar
611 username=askpass-username
612 password=askpass-password
614 askpass: Username for '\''https://bar'\'':
615 askpass: Password for '\''https://askpass-username@bar'\'':
619 test_expect_success 'match multiple configured helpers' '
620 test_config credential.helper "verbatim \"\" \"\"" &&
621 test_config credential.https://example.com.helper "$HELPER" &&
622 check fill <<-\EOF
623 protocol=https
624 host=example.com
625 path=repo.git
627 protocol=https
628 host=example.com
629 username=foo
630 password=bar
632 verbatim: get
633 verbatim: protocol=https
634 verbatim: host=example.com
638 test_expect_success 'match multiple configured helpers with URLs' '
639 test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
640 test_config credential.https://example.com.helper "$HELPER" &&
641 check fill <<-\EOF
642 protocol=https
643 host=example.com
644 path=repo.git
646 protocol=https
647 host=example.com
648 username=foo
649 password=bar
651 verbatim: get
652 verbatim: protocol=https
653 verbatim: host=example.com
657 test_expect_success 'match percent-encoded values' '
658 test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
659 check fill <<-\EOF
660 url=https://example.com/%2566.git
662 protocol=https
663 host=example.com
664 username=foo
665 password=bar
670 test_expect_success 'match percent-encoded UTF-8 values in path' '
671 test_config credential.https://example.com.useHttpPath true &&
672 test_config credential.https://example.com/perĂº.git.helper "$HELPER" &&
673 check fill <<-\EOF
674 url=https://example.com/per%C3%BA.git
676 protocol=https
677 host=example.com
678 path=perĂº.git
679 username=foo
680 password=bar
685 test_expect_success 'match percent-encoded values in username' '
686 test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
687 check fill <<-\EOF
688 url=https://user%2fname@example.com/foo/bar.git
690 protocol=https
691 host=example.com
692 username=foo
693 password=bar
698 test_expect_success 'fetch with multiple path components' '
699 test_unconfig credential.helper &&
700 test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&
701 check fill <<-\EOF
702 url=https://example.com/foo/repo.git
704 protocol=https
705 host=example.com
706 username=foo
707 password=bar
709 verbatim: get
710 verbatim: protocol=https
711 verbatim: host=example.com
715 test_expect_success 'pull username from config' '
716 test_config credential.https://example.com.username foo &&
717 check fill <<-\EOF
718 protocol=https
719 host=example.com
721 protocol=https
722 host=example.com
723 username=foo
724 password=askpass-password
726 askpass: Password for '\''https://foo@example.com'\'':
730 test_expect_success 'honors username from URL over helper (URL)' '
731 test_config credential.https://example.com.username bob &&
732 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
733 check fill <<-\EOF
734 url=https://alice@example.com
736 protocol=https
737 host=example.com
738 username=alice
739 password=bar
741 verbatim: get
742 verbatim: protocol=https
743 verbatim: host=example.com
744 verbatim: username=alice
748 test_expect_success 'honors username from URL over helper (components)' '
749 test_config credential.https://example.com.username bob &&
750 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
751 check fill <<-\EOF
752 protocol=https
753 host=example.com
754 username=alice
756 protocol=https
757 host=example.com
758 username=alice
759 password=bar
761 verbatim: get
762 verbatim: protocol=https
763 verbatim: host=example.com
764 verbatim: username=alice
768 test_expect_success 'last matching username wins' '
769 test_config credential.https://example.com/path.git.username bob &&
770 test_config credential.https://example.com.username alice &&
771 test_config credential.https://example.com.helper "verbatim \"\" bar" &&
772 check fill <<-\EOF
773 url=https://example.com/path.git
775 protocol=https
776 host=example.com
777 username=alice
778 password=bar
780 verbatim: get
781 verbatim: protocol=https
782 verbatim: host=example.com
783 verbatim: username=alice
787 test_expect_success 'http paths can be part of context' '
788 check fill "verbatim foo bar" <<-\EOF &&
789 protocol=https
790 host=example.com
791 path=foo.git
793 protocol=https
794 host=example.com
795 username=foo
796 password=bar
798 verbatim: get
799 verbatim: protocol=https
800 verbatim: host=example.com
802 test_config credential.https://example.com.useHttpPath true &&
803 check fill "verbatim foo bar" <<-\EOF
804 protocol=https
805 host=example.com
806 path=foo.git
808 protocol=https
809 host=example.com
810 path=foo.git
811 username=foo
812 password=bar
814 verbatim: get
815 verbatim: protocol=https
816 verbatim: host=example.com
817 verbatim: path=foo.git
821 test_expect_success 'context uses urlmatch' '
822 test_config "credential.https://*.org.useHttpPath" true &&
823 check fill "verbatim foo bar" <<-\EOF
824 protocol=https
825 host=example.org
826 path=foo.git
828 protocol=https
829 host=example.org
830 path=foo.git
831 username=foo
832 password=bar
834 verbatim: get
835 verbatim: protocol=https
836 verbatim: host=example.org
837 verbatim: path=foo.git
841 test_expect_success 'helpers can abort the process' '
842 test_must_fail git \
843 -c credential.helper=quit \
844 -c credential.helper="verbatim foo bar" \
845 credential fill >stdout 2>stderr <<-\EOF &&
846 protocol=http
847 host=example.com
849 test_must_be_empty stdout &&
850 cat >expect <<-\EOF &&
851 quit: get
852 quit: protocol=http
853 quit: host=example.com
854 fatal: credential helper '\''quit'\'' told us to quit
856 test_cmp expect stderr
859 test_expect_success 'empty helper spec resets helper list' '
860 test_config credential.helper "verbatim file file" &&
861 check fill "" "verbatim cmdline cmdline" <<-\EOF
862 protocol=http
863 host=example.com
865 protocol=http
866 host=example.com
867 username=cmdline
868 password=cmdline
870 verbatim: get
871 verbatim: protocol=http
872 verbatim: host=example.com
876 test_expect_success 'url parser rejects embedded newlines' '
877 test_must_fail git credential fill 2>stderr <<-\EOF &&
878 url=https://one.example.com?%0ahost=two.example.com/
880 cat >expect <<-\EOF &&
881 warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
882 fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
884 test_cmp expect stderr
887 test_expect_success 'host-less URLs are parsed as empty host' '
888 check fill "verbatim foo bar" <<-\EOF
889 url=cert:///path/to/cert.pem
891 protocol=cert
892 host=
893 path=path/to/cert.pem
894 username=foo
895 password=bar
897 verbatim: get
898 verbatim: protocol=cert
899 verbatim: host=
900 verbatim: path=path/to/cert.pem
904 test_expect_success 'credential system refuses to work with missing host' '
905 test_must_fail git credential fill 2>stderr <<-\EOF &&
906 protocol=http
908 cat >expect <<-\EOF &&
909 fatal: refusing to work with credential missing host field
911 test_cmp expect stderr
914 test_expect_success 'credential system refuses to work with missing protocol' '
915 test_must_fail git credential fill 2>stderr <<-\EOF &&
916 host=example.com
918 cat >expect <<-\EOF &&
919 fatal: refusing to work with credential missing protocol field
921 test_cmp expect stderr
924 # usage: check_host_and_path <url> <expected-host> <expected-path>
925 check_host_and_path () {
926 # we always parse the path component, but we need this to make sure it
927 # is passed to the helper
928 test_config credential.useHTTPPath true &&
929 check fill "verbatim user pass" <<-EOF
930 url=$1
932 protocol=https
933 host=$2
934 path=$3
935 username=user
936 password=pass
938 verbatim: get
939 verbatim: protocol=https
940 verbatim: host=$2
941 verbatim: path=$3
945 test_expect_success 'url parser handles bare query marker' '
946 check_host_and_path https://example.com?foo.git example.com ?foo.git
949 test_expect_success 'url parser handles bare fragment marker' '
950 check_host_and_path https://example.com#foo.git example.com "#foo.git"
953 test_expect_success 'url parser not confused by encoded markers' '
954 check_host_and_path https://example.com%23%3f%2f/foo.git \
955 "example.com#?/" foo.git
958 test_expect_success 'credential config with partial URLs' '
959 echo "echo password=yep" | write_script git-credential-yep &&
960 test_write_lines url=https://user@example.com/repo.git >stdin &&
961 for partial in \
962 example.com \
963 user@example.com \
964 https:// \
965 https://example.com \
966 https://example.com/ \
967 https://user@example.com \
968 https://user@example.com/ \
969 https://example.com/repo.git \
970 https://user@example.com/repo.git \
971 /repo.git
973 git -c credential.$partial.helper=yep \
974 credential fill <stdin >stdout &&
975 grep yep stdout ||
976 return 1
977 done &&
979 for partial in \
980 dont.use.this \
981 http:// \
982 /repo
984 git -c credential.$partial.helper=yep \
985 credential fill <stdin >stdout &&
986 ! grep yep stdout ||
987 return 1
988 done &&
990 git -c credential.$partial.helper=yep \
991 -c credential.with%0anewline.username=uh-oh \
992 credential fill <stdin 2>stderr &&
993 test_grep "skipping credential lookup for key" stderr
996 test_done