3 test_description
='basic credential helper tests'
5 .
"$TEST_DIRECTORY"/lib-credential.sh
7 test_expect_success
'setup helper scripts' '
9 whoami=$(echo $0 | sed s/.*git-credential-//)
10 echo >&2 "$whoami: $*"
13 while read key value; do
14 echo >&2 "$whoami: $key=$value"
15 if test -z "${key%%*\[\]}"
18 eval "$key=\"\$$key $value\""
26 write_script git-credential-useless <<-\EOF &&
31 write_script git-credential-quit <<-\EOF &&
36 write_script git-credential-verbatim <<-\EOF &&
40 test -z "$user" || echo username=$user
41 test -z "$pass" || echo password=$pass
44 write_script git-credential-verbatim-cred <<-\EOF &&
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
57 write_script git-credential-verbatim-ephemeral <<-\EOF &&
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
68 write_script git-credential-verbatim-with-expiry <<-\EOF &&
73 test -z "$user" || echo username=$user
74 test -z "$pass" || echo password=$pass
75 test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
81 test_expect_success
'credential_fill invokes helper' '
82 check fill "verbatim foo bar" <<-\EOF
92 verbatim: protocol=http
93 verbatim: host=example.com
97 test_expect_success
'credential_fill invokes helper with credential' '
98 check fill "verbatim-cred Bearer token" <<-\EOF
103 capability[]=authtype
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
122 capability[]=authtype
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
142 capability[]=authtype
148 state[]=verbatim-cred:foo
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
169 useless: protocol=http
170 useless: host=example.com
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
190 useless: capability[]=authtype
191 useless: capability[]=state
192 useless: protocol=http
193 useless: host=example.com
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
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
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
238 capability[]=authtype
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
262 verbatim: protocol=http
263 verbatim: host=example.com
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
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
298 verbatim-with-expiry: get
299 verbatim-with-expiry: protocol=http
300 verbatim-with-expiry: host=example.com
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
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
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
350 useless: protocol=http
351 useless: host=example.com
352 useless: username=foo
353 useless: password=bar
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
368 password_expiry_utc=9999999999
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
386 oauth_refresh_token=xyzzy
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
408 test_expect_success
'credential_approve does not store expired password' '
409 check approve useless <<-\EOF
414 password_expiry_utc=5
420 test_expect_success
'credential_reject calls all helpers' '
421 check reject useless "verbatim one two" <<-\EOF
429 useless: protocol=http
430 useless: host=example.com
431 useless: username=foo
432 useless: password=bar
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
447 password_expiry_utc=5
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
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
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
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' '
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' '
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"
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 &&
560 username=alternate-value
561 password=alternate-value
563 alternate askpass invoked
564 alternate askpass invoked
574 test_expect_success
'respect configured credentials' '
575 test_config credential.helper "$HELPER" &&
588 test_expect_success
'match configured credential' '
589 test_config credential.https://example.com.helper "$HELPER" &&
603 test_expect_success
'do not match configured credential' '
604 test_config credential.https://foo.helper "$HELPER" &&
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" &&
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" &&
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" &&
660 url=https://example.com/%2566.git
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" &&
674 url=https://example.com/per%C3%BA.git
685 test_expect_success
'match percent-encoded values in username' '
686 test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
688 url=https://user%2fname@example.com/foo/bar.git
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" &&
702 url=https://example.com/foo/repo.git
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 &&
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" &&
734 url=https://alice@example.com
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" &&
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" &&
773 url=https://example.com/path.git
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 &&
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
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
835 verbatim: protocol=https
836 verbatim: host=example.org
837 verbatim: path=foo.git
841 test_expect_success
'helpers can abort the process' '
843 -c credential.helper=quit \
844 -c credential.helper="verbatim foo bar" \
845 credential fill >stdout 2>stderr <<-\EOF &&
849 test_must_be_empty stdout &&
850 cat >expect <<-\EOF &&
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
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
893 path=path/to/cert.pem
898 verbatim: protocol=cert
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 &&
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 &&
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
939 verbatim: protocol=https
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 &&
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 \
973 git -c credential.$partial.helper=yep \
974 credential fill <stdin >stdout &&
984 git -c credential.$partial.helper=yep \
985 credential fill <stdin >stdout &&
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