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"
20 write_script git-credential-useless <<-\EOF &&
25 write_script git-credential-quit <<-\EOF &&
30 write_script git-credential-verbatim <<-\EOF &&
34 test -z "$user" || echo username=$user
35 test -z "$pass" || echo password=$pass
38 write_script git-credential-verbatim-with-expiry <<-\EOF &&
43 test -z "$user" || echo username=$user
44 test -z "$pass" || echo password=$pass
45 test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
51 test_expect_success
'credential_fill invokes helper' '
52 check fill "verbatim foo bar" <<-\EOF
62 verbatim: protocol=http
63 verbatim: host=example.com
67 test_expect_success
'credential_fill invokes multiple helpers' '
68 check fill useless "verbatim foo bar" <<-\EOF
78 useless: protocol=http
79 useless: host=example.com
81 verbatim: protocol=http
82 verbatim: host=example.com
86 test_expect_success
'credential_fill stops when we get a full response' '
87 check fill "verbatim one two" "verbatim three four" <<-\EOF
97 verbatim: protocol=http
98 verbatim: host=example.com
102 test_expect_success
'credential_fill continues through partial response' '
103 check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
113 verbatim: protocol=http
114 verbatim: host=example.com
116 verbatim: protocol=http
117 verbatim: host=example.com
118 verbatim: username=one
122 test_expect_success
'credential_fill populates password_expiry_utc' '
123 check fill "verbatim-with-expiry one two 9999999999" <<-\EOF
131 password_expiry_utc=9999999999
133 verbatim-with-expiry: get
134 verbatim-with-expiry: protocol=http
135 verbatim-with-expiry: host=example.com
139 test_expect_success
'credential_fill ignores expired password' '
140 check fill "verbatim-with-expiry one two 5" "verbatim three four" <<-\EOF
149 verbatim-with-expiry: get
150 verbatim-with-expiry: protocol=http
151 verbatim-with-expiry: host=example.com
153 verbatim: protocol=http
154 verbatim: host=example.com
155 verbatim: username=one
159 test_expect_success
'credential_fill passes along metadata' '
160 check fill "verbatim one two" <<-\EOF
172 verbatim: protocol=ftp
173 verbatim: host=example.com
174 verbatim: path=foo.git
178 test_expect_success
'credential_approve calls all helpers' '
179 check approve useless "verbatim one two" <<-\EOF
187 useless: protocol=http
188 useless: host=example.com
189 useless: username=foo
190 useless: password=bar
192 verbatim: protocol=http
193 verbatim: host=example.com
194 verbatim: username=foo
195 verbatim: password=bar
199 test_expect_success
'credential_approve stores password expiry' '
200 check approve useless <<-\EOF
205 password_expiry_utc=9999999999
209 useless: protocol=http
210 useless: host=example.com
211 useless: username=foo
212 useless: password=bar
213 useless: password_expiry_utc=9999999999
217 test_expect_success
'credential_approve stores oauth refresh token' '
218 check approve useless <<-\EOF
223 oauth_refresh_token=xyzzy
227 useless: protocol=http
228 useless: host=example.com
229 useless: username=foo
230 useless: password=bar
231 useless: oauth_refresh_token=xyzzy
235 test_expect_success
'do not bother storing password-less credential' '
236 check approve useless <<-\EOF
245 test_expect_success
'credential_approve does not store expired password' '
246 check approve useless <<-\EOF
251 password_expiry_utc=5
257 test_expect_success
'credential_reject calls all helpers' '
258 check reject useless "verbatim one two" <<-\EOF
266 useless: protocol=http
267 useless: host=example.com
268 useless: username=foo
269 useless: password=bar
271 verbatim: protocol=http
272 verbatim: host=example.com
273 verbatim: username=foo
274 verbatim: password=bar
278 test_expect_success
'credential_reject erases credential regardless of expiry' '
279 check reject useless <<-\EOF
284 password_expiry_utc=5
288 useless: protocol=http
289 useless: host=example.com
290 useless: username=foo
291 useless: password=bar
292 useless: password_expiry_utc=5
296 test_expect_success
'usernames can be preserved' '
297 check fill "verbatim \"\" three" <<-\EOF
308 verbatim: protocol=http
309 verbatim: host=example.com
310 verbatim: username=one
314 test_expect_success
'usernames can be overridden' '
315 check fill "verbatim two three" <<-\EOF
326 verbatim: protocol=http
327 verbatim: host=example.com
328 verbatim: username=one
332 test_expect_success
'do not bother completing already-full credential' '
333 check fill "verbatim three four" <<-\EOF
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' '
357 username=askpass-username
358 password=askpass-password
360 askpass: Username for '\''http://example.com'\'':
361 askpass: Password for '\''http://askpass-username@example.com'\'':
365 test_expect_success
'internal getpass does not ask for known username' '
374 password=askpass-password
376 askpass: Password for '\''http://foo@example.com'\'':
380 test_expect_success
'git-credential respects core.askPass' '
381 write_script alternate-askpass <<-\EOF &&
382 echo >&2 "alternate askpass invoked"
385 test_config core.askpass "$PWD/alternate-askpass" &&
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 &&
397 username=alternate-value
398 password=alternate-value
400 alternate askpass invoked
401 alternate askpass invoked
411 test_expect_success
'respect configured credentials' '
412 test_config credential.helper "$HELPER" &&
425 test_expect_success
'match configured credential' '
426 test_config credential.https://example.com.helper "$HELPER" &&
440 test_expect_success
'do not match configured credential' '
441 test_config credential.https://foo.helper "$HELPER" &&
448 username=askpass-username
449 password=askpass-password
451 askpass: Username for '\''https://bar'\'':
452 askpass: Password for '\''https://askpass-username@bar'\'':
456 test_expect_success
'match multiple configured helpers' '
457 test_config credential.helper "verbatim \"\" \"\"" &&
458 test_config credential.https://example.com.helper "$HELPER" &&
470 verbatim: protocol=https
471 verbatim: host=example.com
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" &&
489 verbatim: protocol=https
490 verbatim: host=example.com
494 test_expect_success
'match percent-encoded values' '
495 test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
497 url=https://example.com/%2566.git
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" &&
511 url=https://example.com/per%C3%BA.git
522 test_expect_success
'match percent-encoded values in username' '
523 test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
525 url=https://user%2fname@example.com/foo/bar.git
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" &&
539 url=https://example.com/foo/repo.git
547 verbatim: protocol=https
548 verbatim: host=example.com
552 test_expect_success
'pull username from config' '
553 test_config credential.https://example.com.username foo &&
561 password=askpass-password
563 askpass: Password for '\''https://foo@example.com'\'':
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" &&
571 url=https://alice@example.com
579 verbatim: protocol=https
580 verbatim: host=example.com
581 verbatim: username=alice
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" &&
599 verbatim: protocol=https
600 verbatim: host=example.com
601 verbatim: username=alice
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" &&
610 url=https://example.com/path.git
618 verbatim: protocol=https
619 verbatim: host=example.com
620 verbatim: username=alice
624 test_expect_success
'http paths can be part of context' '
625 check fill "verbatim foo bar" <<-\EOF &&
636 verbatim: protocol=https
637 verbatim: host=example.com
639 test_config credential.https://example.com.useHttpPath true &&
640 check fill "verbatim foo bar" <<-\EOF
652 verbatim: protocol=https
653 verbatim: host=example.com
654 verbatim: path=foo.git
658 test_expect_success
'context uses urlmatch' '
659 test_config "credential.https://*.org.useHttpPath" true &&
660 check fill "verbatim foo bar" <<-\EOF
672 verbatim: protocol=https
673 verbatim: host=example.org
674 verbatim: path=foo.git
678 test_expect_success
'helpers can abort the process' '
680 -c credential.helper=quit \
681 -c credential.helper="verbatim foo bar" \
682 credential fill >stdout 2>stderr <<-\EOF &&
686 test_must_be_empty stdout &&
687 cat >expect <<-\EOF &&
690 quit: host=example.com
691 fatal: credential helper '\''quit'\'' told us to quit
693 test_cmp expect stderr
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
708 verbatim: protocol=http
709 verbatim: host=example.com
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/
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/
721 test_cmp expect stderr
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
730 path=path/to/cert.pem
735 verbatim: protocol=cert
737 verbatim: path=path/to/cert.pem
741 test_expect_success
'credential system refuses to work with missing host' '
742 test_must_fail git credential fill 2>stderr <<-\EOF &&
745 cat >expect <<-\EOF &&
746 fatal: refusing to work with credential missing host field
748 test_cmp expect stderr
751 test_expect_success
'credential system refuses to work with missing protocol' '
752 test_must_fail git credential fill 2>stderr <<-\EOF &&
755 cat >expect <<-\EOF &&
756 fatal: refusing to work with credential missing protocol field
758 test_cmp expect stderr
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
776 verbatim: protocol=https
782 test_expect_success
'url parser handles bare query marker' '
783 check_host_and_path https://example.com?foo.git example.com ?foo.git
786 test_expect_success
'url parser handles bare fragment marker' '
787 check_host_and_path https://example.com#foo.git example.com "#foo.git"
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
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 &&
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 \
810 git -c credential.$partial.helper=yep \
811 credential fill <stdin >stdout &&
821 git -c credential.$partial.helper=yep \
822 credential fill <stdin >stdout &&
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