| # Shell library for testing credential handling including helpers. See t0302 |
| # for an example of testing a specific helper. |
| |
| # Try a set of credential helpers; the expected stdin, |
| # stdout and stderr should be provided on stdin, |
| # separated by "--". |
| check() { |
| credential_opts= |
| credential_cmd=$1 |
| shift |
| for arg in "$@"; do |
| credential_opts="$credential_opts -c credential.helper='$arg'" |
| done |
| read_chunk >stdin && |
| read_chunk >expect-stdout && |
| read_chunk >expect-stderr && |
| if ! eval "git $credential_opts credential $credential_cmd <stdin >stdout 2>stderr"; then |
| echo "git credential failed with code $?" && |
| cat stderr && |
| false |
| fi && |
| test_cmp expect-stdout stdout && |
| test_cmp expect-stderr stderr |
| } |
| |
| read_chunk() { |
| while read line; do |
| case "$line" in |
| --) break ;; |
| *) echo "$line" ;; |
| esac |
| done |
| } |
| |
| # Clear any residual data from previous tests. We only |
| # need this when testing third-party helpers which read and |
| # write outside of our trash-directory sandbox. |
| # |
| # Don't bother checking for success here, as it is |
| # outside the scope of tests and represents a best effort to |
| # clean up after ourselves. |
| helper_test_clean() { |
| reject $1 https example.com store-user |
| reject $1 https example.com user1 |
| reject $1 https example.com user2 |
| reject $1 https example.com user-expiry |
| reject $1 https example.com user-expiry-overwrite |
| reject $1 https example.com user4 |
| reject $1 https example.com user-distinct-pass |
| reject $1 https example.com user-overwrite |
| reject $1 https example.com user-erase1 |
| reject $1 https example.com user-erase2 |
| reject $1 https victim.example.com user |
| reject $1 http path.tld user |
| reject $1 https timeout.tld user |
| reject $1 https sso.tld |
| } |
| |
| reject() { |
| ( |
| echo protocol=$2 |
| echo host=$3 |
| echo username=$4 |
| ) | git -c credential.helper=$1 credential reject |
| } |
| |
| helper_test() { |
| HELPER=$1 |
| |
| test_expect_success "helper ($HELPER) has no existing data" ' |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| -- |
| protocol=https |
| host=example.com |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''https://example.com'\'': |
| askpass: Password for '\''https://askpass-username@example.com'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) stores password" ' |
| check approve $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=store-user |
| password=store-pass |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) can retrieve password" ' |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| -- |
| protocol=https |
| host=example.com |
| username=store-user |
| password=store-pass |
| -- |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) requires matching protocol" ' |
| check fill $HELPER <<-\EOF |
| protocol=http |
| host=example.com |
| -- |
| protocol=http |
| host=example.com |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''http://example.com'\'': |
| askpass: Password for '\''http://askpass-username@example.com'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) requires matching host" ' |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=other.tld |
| -- |
| protocol=https |
| host=other.tld |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''https://other.tld'\'': |
| askpass: Password for '\''https://askpass-username@other.tld'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) requires matching username" ' |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=other |
| -- |
| protocol=https |
| host=example.com |
| username=other |
| password=askpass-password |
| -- |
| askpass: Password for '\''https://other@example.com'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) requires matching path" ' |
| test_config credential.usehttppath true && |
| check approve $HELPER <<-\EOF && |
| protocol=http |
| host=path.tld |
| path=foo.git |
| username=user |
| password=pass |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=http |
| host=path.tld |
| path=bar.git |
| -- |
| protocol=http |
| host=path.tld |
| path=bar.git |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''http://path.tld/bar.git'\'': |
| askpass: Password for '\''http://askpass-username@path.tld/bar.git'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) overwrites on store" ' |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-overwrite |
| password=pass1 |
| EOF |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-overwrite |
| password=pass2 |
| EOF |
| check fill $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-overwrite |
| -- |
| protocol=https |
| host=example.com |
| username=user-overwrite |
| password=pass2 |
| EOF |
| check reject $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-overwrite |
| password=pass2 |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user-overwrite |
| -- |
| protocol=https |
| host=example.com |
| username=user-overwrite |
| password=askpass-password |
| -- |
| askpass: Password for '\''https://user-overwrite@example.com'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) can forget host" ' |
| check reject $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| -- |
| protocol=https |
| host=example.com |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''https://example.com'\'': |
| askpass: Password for '\''https://askpass-username@example.com'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) can store multiple users" ' |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user1 |
| password=pass1 |
| EOF |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user2 |
| password=pass2 |
| EOF |
| check fill $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user1 |
| -- |
| protocol=https |
| host=example.com |
| username=user1 |
| password=pass1 |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user2 |
| -- |
| protocol=https |
| host=example.com |
| username=user2 |
| password=pass2 |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) does not erase a password distinct from input" ' |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-distinct-pass |
| password=pass1 |
| EOF |
| check reject $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-distinct-pass |
| password=pass2 |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user-distinct-pass |
| -- |
| protocol=https |
| host=example.com |
| username=user-distinct-pass |
| password=pass1 |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) can forget user" ' |
| check reject $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user1 |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user1 |
| -- |
| protocol=https |
| host=example.com |
| username=user1 |
| password=askpass-password |
| -- |
| askpass: Password for '\''https://user1@example.com'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) remembers other user" ' |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user2 |
| -- |
| protocol=https |
| host=example.com |
| username=user2 |
| password=pass2 |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) can store empty username" ' |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=sso.tld |
| username= |
| password= |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=sso.tld |
| -- |
| protocol=https |
| host=sso.tld |
| username= |
| password= |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) erases all matching credentials" ' |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-erase1 |
| password=pass1 |
| EOF |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-erase2 |
| password=pass1 |
| EOF |
| check reject $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| -- |
| protocol=https |
| host=example.com |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''https://example.com'\'': |
| askpass: Password for '\''https://askpass-username@example.com'\'': |
| EOF |
| ' |
| |
| : ${GIT_TEST_LONG_CRED_BUFFER:=1024} |
| # 23 bytes accounts for "wwwauth[]=basic realm=" plus NUL |
| LONG_VALUE_LEN=$((GIT_TEST_LONG_CRED_BUFFER - 23)) |
| LONG_VALUE=$(perl -e 'print "a" x shift' $LONG_VALUE_LEN) |
| |
| test_expect_success "helper ($HELPER) not confused by long header" ' |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=victim.example.com |
| username=user |
| password=to-be-stolen |
| EOF |
| |
| check fill $HELPER <<-EOF |
| protocol=https |
| host=badguy.example.com |
| wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com |
| -- |
| protocol=https |
| host=badguy.example.com |
| username=askpass-username |
| password=askpass-password |
| wwwauth[]=basic realm=${LONG_VALUE}host=victim.example.com |
| -- |
| askpass: Username for '\''https://badguy.example.com'\'': |
| askpass: Password for '\''https://askpass-username@badguy.example.com'\'': |
| EOF |
| ' |
| } |
| |
| helper_test_timeout() { |
| HELPER="$*" |
| |
| test_expect_success "helper ($HELPER) times out" ' |
| check approve "$HELPER" <<-\EOF && |
| protocol=https |
| host=timeout.tld |
| username=user |
| password=pass |
| EOF |
| sleep 2 && |
| check fill "$HELPER" <<-\EOF |
| protocol=https |
| host=timeout.tld |
| -- |
| protocol=https |
| host=timeout.tld |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''https://timeout.tld'\'': |
| askpass: Password for '\''https://askpass-username@timeout.tld'\'': |
| EOF |
| ' |
| } |
| |
| helper_test_password_expiry_utc() { |
| HELPER=$1 |
| |
| test_expect_success "helper ($HELPER) stores password_expiry_utc" ' |
| check approve $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user-expiry |
| password=pass |
| password_expiry_utc=9999999999 |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) gets password_expiry_utc" ' |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user-expiry |
| -- |
| protocol=https |
| host=example.com |
| username=user-expiry |
| password=pass |
| password_expiry_utc=9999999999 |
| -- |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) overwrites when password_expiry_utc changes" ' |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-expiry-overwrite |
| password=pass1 |
| password_expiry_utc=9999999998 |
| EOF |
| check approve $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-expiry-overwrite |
| password=pass2 |
| password_expiry_utc=9999999999 |
| EOF |
| check fill $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-expiry-overwrite |
| -- |
| protocol=https |
| host=example.com |
| username=user-expiry-overwrite |
| password=pass2 |
| password_expiry_utc=9999999999 |
| EOF |
| check reject $HELPER <<-\EOF && |
| protocol=https |
| host=example.com |
| username=user-expiry-overwrite |
| password=pass2 |
| EOF |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user-expiry-overwrite |
| -- |
| protocol=https |
| host=example.com |
| username=user-expiry-overwrite |
| password=askpass-password |
| -- |
| askpass: Password for '\''https://user-expiry-overwrite@example.com'\'': |
| EOF |
| ' |
| } |
| |
| helper_test_oauth_refresh_token() { |
| HELPER=$1 |
| |
| test_expect_success "helper ($HELPER) stores oauth_refresh_token" ' |
| check approve $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user4 |
| password=pass |
| oauth_refresh_token=xyzzy |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) gets oauth_refresh_token" ' |
| check fill $HELPER <<-\EOF |
| protocol=https |
| host=example.com |
| username=user4 |
| -- |
| protocol=https |
| host=example.com |
| username=user4 |
| password=pass |
| oauth_refresh_token=xyzzy |
| -- |
| EOF |
| ' |
| } |
| |
| helper_test_authtype() { |
| HELPER=$1 |
| |
| test_expect_success "helper ($HELPER) stores authtype and credential" ' |
| check approve $HELPER <<-\EOF |
| capability[]=authtype |
| authtype=Bearer |
| credential=random-token |
| protocol=https |
| host=git.example.com |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) gets authtype and credential" ' |
| check fill $HELPER <<-\EOF |
| capability[]=authtype |
| protocol=https |
| host=git.example.com |
| -- |
| capability[]=authtype |
| authtype=Bearer |
| credential=random-token |
| protocol=https |
| host=git.example.com |
| -- |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) stores authtype and credential with username" ' |
| check approve $HELPER <<-\EOF |
| capability[]=authtype |
| authtype=Bearer |
| credential=other-token |
| protocol=https |
| host=git.example.com |
| username=foobar |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) gets authtype and credential with username" ' |
| check fill $HELPER <<-\EOF |
| capability[]=authtype |
| protocol=https |
| host=git.example.com |
| username=foobar |
| -- |
| capability[]=authtype |
| authtype=Bearer |
| credential=other-token |
| protocol=https |
| host=git.example.com |
| username=foobar |
| -- |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) does not get authtype and credential with different username" ' |
| check fill $HELPER <<-\EOF |
| capability[]=authtype |
| protocol=https |
| host=git.example.com |
| username=barbaz |
| -- |
| protocol=https |
| host=git.example.com |
| username=barbaz |
| password=askpass-password |
| -- |
| askpass: Password for '\''https://barbaz@git.example.com'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) does not store ephemeral authtype and credential" ' |
| check approve $HELPER <<-\EOF && |
| capability[]=authtype |
| authtype=Bearer |
| credential=git2-token |
| protocol=https |
| host=git2.example.com |
| ephemeral=1 |
| EOF |
| |
| check fill $HELPER <<-\EOF |
| capability[]=authtype |
| protocol=https |
| host=git2.example.com |
| -- |
| protocol=https |
| host=git2.example.com |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''https://git2.example.com'\'': |
| askpass: Password for '\''https://askpass-username@git2.example.com'\'': |
| EOF |
| ' |
| |
| test_expect_success "helper ($HELPER) does not store ephemeral username and password" ' |
| check approve $HELPER <<-\EOF && |
| capability[]=authtype |
| protocol=https |
| host=git2.example.com |
| user=barbaz |
| password=secret |
| ephemeral=1 |
| EOF |
| |
| check fill $HELPER <<-\EOF |
| capability[]=authtype |
| protocol=https |
| host=git2.example.com |
| -- |
| protocol=https |
| host=git2.example.com |
| username=askpass-username |
| password=askpass-password |
| -- |
| askpass: Username for '\''https://git2.example.com'\'': |
| askpass: Password for '\''https://askpass-username@git2.example.com'\'': |
| EOF |
| ' |
| } |
| |
| write_script askpass <<\EOF |
| echo >&2 askpass: $* |
| what=$(echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z) |
| echo "askpass-$what" |
| EOF |
| GIT_ASKPASS="$PWD/askpass" |
| export GIT_ASKPASS |