671 lines
14 KiB
Bash
671 lines
14 KiB
Bash
# 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
|