2011-12-10 10:31:11 +00:00
|
|
|
#!/bin/sh
|
|
|
|
|
|
|
|
test_description='basic credential helper tests'
|
|
|
|
. ./test-lib.sh
|
|
|
|
. "$TEST_DIRECTORY"/lib-credential.sh
|
|
|
|
|
|
|
|
test_expect_success 'setup helper scripts' '
|
|
|
|
cat >dump <<-\EOF &&
|
2014-04-28 12:57:30 +00:00
|
|
|
whoami=$(echo $0 | sed s/.*git-credential-//)
|
2011-12-10 10:31:11 +00:00
|
|
|
echo >&2 "$whoami: $*"
|
t0300: work around bug in dash 0.5.6
The construct 'while IFS== read' makes dash 0.5.6 execute
read without changing IFS, which results in test breakages
all over the place in t0300. Neither dash 0.5.5.1 and older
nor dash 0.5.7 and newer are affected: The problem was
introduded resp. fixed by the commits
55c46b7 ([BUILTIN] Honor tab as IFS whitespace when
splitting fields in readcmd, 2009-08-11)
1d806ac ([VAR] Do not poplocalvars prematurely on regular
utilities, 2010-05-27)
in http://git.kernel.org/?p=utils/dash/dash.git
Putting 'IFS==' before that line makes all versions of dash
work.
This looks like a dash bug, not a misinterpretation of the
standard. However, it's worth working around for two
reasons. One, this version of dash was released in Fedora
14-16, so the bug is found in the wild. And two, at least
one other shell, Solaris /bin/sh, choked on this by
persisting IFS after the read invocation. That is not a
shell we usually care about, and I think this use of IFS is
acceptable by POSIX (which allows other behavior near
"special builtins", but "read" is not one of those). But it
seems that this may be a subtle, not-well-tested case for
some shells. Given that the workaround is so simple, it's
worth just being defensive.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-03-03 00:37:35 +00:00
|
|
|
OIFS=$IFS
|
|
|
|
IFS==
|
|
|
|
while read key value; do
|
2011-12-10 10:31:11 +00:00
|
|
|
echo >&2 "$whoami: $key=$value"
|
credential: gate new fields on capability
We support the new credential and authtype fields, but we lack a way to
indicate to a credential helper that we'd like them to be used. Without
some sort of indication, the credential helper doesn't know if it should
try to provide us a username and password, or a pre-encoded credential.
For example, the helper might prefer a more restricted Bearer token if
pre-encoded credentials are possible, but might have to fall back to
more general username and password if not.
Let's provide a simple way to indicate whether Git (or, for that matter,
the helper) is capable of understanding the authtype and credential
fields. We send this capability when we generate a request, and the
other side may reply to indicate to us that it does, too.
For now, don't enable sending capabilities for the HTTP code. In a
future commit, we'll introduce appropriate handling for that code,
which requires more in-depth work.
The logic for determining whether a capability is supported may seem
complex, but it is not. At each stage, we emit the capability to the
following stage if all preceding stages have declared it. Thus, if the
caller to git credential fill didn't declare it, then we won't send it
to the helper, and if fill's caller did send but the helper doesn't
understand it, then we won't send it on in the response. If we're an
internal user, then we know about all capabilities and will request
them.
For "git credential approve" and "git credential reject", we set the
helper capability before calling the helper, since we assume that the
input we're getting from the external program comes from a previous call
to "git credential fill", and thus we'll invoke send a capability to the
helper if and only if we got one from the standard input, which is the
correct behavior.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 00:02:29 +00:00
|
|
|
if test -z "${key%%*\[\]}"
|
|
|
|
then
|
|
|
|
key=${key%%\[\]}
|
|
|
|
eval "$key=\"\$$key $value\""
|
|
|
|
else
|
|
|
|
eval "$key=$value"
|
|
|
|
fi
|
2011-12-10 10:31:11 +00:00
|
|
|
done
|
t0300: work around bug in dash 0.5.6
The construct 'while IFS== read' makes dash 0.5.6 execute
read without changing IFS, which results in test breakages
all over the place in t0300. Neither dash 0.5.5.1 and older
nor dash 0.5.7 and newer are affected: The problem was
introduded resp. fixed by the commits
55c46b7 ([BUILTIN] Honor tab as IFS whitespace when
splitting fields in readcmd, 2009-08-11)
1d806ac ([VAR] Do not poplocalvars prematurely on regular
utilities, 2010-05-27)
in http://git.kernel.org/?p=utils/dash/dash.git
Putting 'IFS==' before that line makes all versions of dash
work.
This looks like a dash bug, not a misinterpretation of the
standard. However, it's worth working around for two
reasons. One, this version of dash was released in Fedora
14-16, so the bug is found in the wild. And two, at least
one other shell, Solaris /bin/sh, choked on this by
persisting IFS after the read invocation. That is not a
shell we usually care about, and I think this use of IFS is
acceptable by POSIX (which allows other behavior near
"special builtins", but "read" is not one of those). But it
seems that this may be a subtle, not-well-tested case for
some shells. Given that the workaround is so simple, it's
worth just being defensive.
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-03-03 00:37:35 +00:00
|
|
|
IFS=$OIFS
|
2011-12-10 10:31:11 +00:00
|
|
|
EOF
|
|
|
|
|
2012-02-04 06:30:18 +00:00
|
|
|
write_script git-credential-useless <<-\EOF &&
|
2011-12-10 10:31:11 +00:00
|
|
|
. ./dump
|
|
|
|
exit 0
|
|
|
|
EOF
|
|
|
|
|
2020-04-19 05:36:02 +00:00
|
|
|
write_script git-credential-quit <<-\EOF &&
|
|
|
|
. ./dump
|
|
|
|
echo quit=1
|
|
|
|
EOF
|
|
|
|
|
2012-02-04 06:30:18 +00:00
|
|
|
write_script git-credential-verbatim <<-\EOF &&
|
2011-12-10 10:31:11 +00:00
|
|
|
user=$1; shift
|
|
|
|
pass=$1; shift
|
|
|
|
. ./dump
|
|
|
|
test -z "$user" || echo username=$user
|
|
|
|
test -z "$pass" || echo password=$pass
|
|
|
|
EOF
|
|
|
|
|
credential: gate new fields on capability
We support the new credential and authtype fields, but we lack a way to
indicate to a credential helper that we'd like them to be used. Without
some sort of indication, the credential helper doesn't know if it should
try to provide us a username and password, or a pre-encoded credential.
For example, the helper might prefer a more restricted Bearer token if
pre-encoded credentials are possible, but might have to fall back to
more general username and password if not.
Let's provide a simple way to indicate whether Git (or, for that matter,
the helper) is capable of understanding the authtype and credential
fields. We send this capability when we generate a request, and the
other side may reply to indicate to us that it does, too.
For now, don't enable sending capabilities for the HTTP code. In a
future commit, we'll introduce appropriate handling for that code,
which requires more in-depth work.
The logic for determining whether a capability is supported may seem
complex, but it is not. At each stage, we emit the capability to the
following stage if all preceding stages have declared it. Thus, if the
caller to git credential fill didn't declare it, then we won't send it
to the helper, and if fill's caller did send but the helper doesn't
understand it, then we won't send it on in the response. If we're an
internal user, then we know about all capabilities and will request
them.
For "git credential approve" and "git credential reject", we set the
helper capability before calling the helper, since we assume that the
input we're getting from the external program comes from a previous call
to "git credential fill", and thus we'll invoke send a capability to the
helper if and only if we got one from the standard input, which is the
correct behavior.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 00:02:29 +00:00
|
|
|
write_script git-credential-verbatim-cred <<-\EOF &&
|
|
|
|
authtype=$1; shift
|
|
|
|
credential=$1; shift
|
|
|
|
. ./dump
|
|
|
|
echo capability[]=authtype
|
|
|
|
test -z "${capability##*authtype*}" || exit 0
|
|
|
|
test -z "$authtype" || echo authtype=$authtype
|
|
|
|
test -z "$credential" || echo credential=$credential
|
|
|
|
EOF
|
|
|
|
|
credential: new attribute password_expiry_utc
Some passwords have an expiry date known at generation. This may be
years away for a personal access token or hours for an OAuth access
token.
When multiple credential helpers are configured, `credential fill` tries
each helper in turn until it has a username and password, returning
early. If Git authentication succeeds, `credential approve`
stores the successful credential in all helpers. If authentication
fails, `credential reject` erases matching credentials in all helpers.
Helpers implement corresponding operations: get, store, erase.
The credential protocol has no expiry attribute, so helpers cannot
store expiry information. Even if a helper returned an improvised
expiry attribute, git credential discards unrecognised attributes
between operations and between helpers.
This is a particular issue when a storage helper and a
credential-generating helper are configured together:
[credential]
helper = storage # eg. cache or osxkeychain
helper = generate # eg. oauth
`credential approve` stores the generated credential in both helpers
without expiry information. Later `credential fill` may return an
expired credential from storage. There is no workaround, no matter how
clever the second helper. The user sees authentication fail (a retry
will succeed).
Introduce a password expiry attribute. In `credential fill`, ignore
expired passwords and continue to query subsequent helpers.
In the example above, `credential fill` ignores the expired password
and a fresh credential is generated. If authentication succeeds,
`credential approve` replaces the expired password in storage.
If authentication fails, the expired credential is erased by
`credential reject`. It is unnecessary but harmless for storage
helpers to self prune expired credentials.
Add support for the new attribute to credential-cache.
Eventually, I hope to see support in other popular storage helpers.
Example usage in a credential-generating helper
https://github.com/hickford/git-credential-oauth/pull/16
Signed-off-by: M Hickford <mirth.hickford@gmail.com>
Reviewed-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-02-18 06:32:57 +00:00
|
|
|
write_script git-credential-verbatim-with-expiry <<-\EOF &&
|
|
|
|
user=$1; shift
|
|
|
|
pass=$1; shift
|
|
|
|
pexpiry=$1; shift
|
|
|
|
. ./dump
|
|
|
|
test -z "$user" || echo username=$user
|
|
|
|
test -z "$pass" || echo password=$pass
|
|
|
|
test -z "$pexpiry" || echo password_expiry_utc=$pexpiry
|
|
|
|
EOF
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
PATH="$PWD:$PATH"
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'credential_fill invokes helper' '
|
|
|
|
check fill "verbatim foo bar" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: gate new fields on capability
We support the new credential and authtype fields, but we lack a way to
indicate to a credential helper that we'd like them to be used. Without
some sort of indication, the credential helper doesn't know if it should
try to provide us a username and password, or a pre-encoded credential.
For example, the helper might prefer a more restricted Bearer token if
pre-encoded credentials are possible, but might have to fall back to
more general username and password if not.
Let's provide a simple way to indicate whether Git (or, for that matter,
the helper) is capable of understanding the authtype and credential
fields. We send this capability when we generate a request, and the
other side may reply to indicate to us that it does, too.
For now, don't enable sending capabilities for the HTTP code. In a
future commit, we'll introduce appropriate handling for that code,
which requires more in-depth work.
The logic for determining whether a capability is supported may seem
complex, but it is not. At each stage, we emit the capability to the
following stage if all preceding stages have declared it. Thus, if the
caller to git credential fill didn't declare it, then we won't send it
to the helper, and if fill's caller did send but the helper doesn't
understand it, then we won't send it on in the response. If we're an
internal user, then we know about all capabilities and will request
them.
For "git credential approve" and "git credential reject", we set the
helper capability before calling the helper, since we assume that the
input we're getting from the external program comes from a previous call
to "git credential fill", and thus we'll invoke send a capability to the
helper if and only if we got one from the standard input, which is the
correct behavior.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 00:02:29 +00:00
|
|
|
test_expect_success 'credential_fill invokes helper with credential' '
|
|
|
|
check fill "verbatim-cred Bearer token" <<-\EOF
|
|
|
|
capability[]=authtype
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
capability[]=authtype
|
|
|
|
authtype=Bearer
|
|
|
|
credential=token
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
verbatim-cred: get
|
|
|
|
verbatim-cred: capability[]=authtype
|
|
|
|
verbatim-cred: protocol=http
|
|
|
|
verbatim-cred: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
test_expect_success 'credential_fill invokes multiple helpers' '
|
|
|
|
check fill useless "verbatim foo bar" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
useless: get
|
2020-04-19 03:47:30 +00:00
|
|
|
useless: protocol=http
|
|
|
|
useless: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
verbatim: get
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: gate new fields on capability
We support the new credential and authtype fields, but we lack a way to
indicate to a credential helper that we'd like them to be used. Without
some sort of indication, the credential helper doesn't know if it should
try to provide us a username and password, or a pre-encoded credential.
For example, the helper might prefer a more restricted Bearer token if
pre-encoded credentials are possible, but might have to fall back to
more general username and password if not.
Let's provide a simple way to indicate whether Git (or, for that matter,
the helper) is capable of understanding the authtype and credential
fields. We send this capability when we generate a request, and the
other side may reply to indicate to us that it does, too.
For now, don't enable sending capabilities for the HTTP code. In a
future commit, we'll introduce appropriate handling for that code,
which requires more in-depth work.
The logic for determining whether a capability is supported may seem
complex, but it is not. At each stage, we emit the capability to the
following stage if all preceding stages have declared it. Thus, if the
caller to git credential fill didn't declare it, then we won't send it
to the helper, and if fill's caller did send but the helper doesn't
understand it, then we won't send it on in the response. If we're an
internal user, then we know about all capabilities and will request
them.
For "git credential approve" and "git credential reject", we set the
helper capability before calling the helper, since we assume that the
input we're getting from the external program comes from a previous call
to "git credential fill", and thus we'll invoke send a capability to the
helper if and only if we got one from the standard input, which is the
correct behavior.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 00:02:29 +00:00
|
|
|
test_expect_success 'credential_fill response does not get capabilities when helpers are incapable' '
|
|
|
|
check fill useless "verbatim foo bar" <<-\EOF
|
|
|
|
capability[]=authtype
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
useless: get
|
|
|
|
useless: capability[]=authtype
|
|
|
|
useless: protocol=http
|
|
|
|
useless: host=example.com
|
|
|
|
verbatim: get
|
|
|
|
verbatim: capability[]=authtype
|
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'credential_fill response does not get capabilities when caller is incapable' '
|
|
|
|
check fill "verbatim-cred Bearer token" <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
verbatim-cred: get
|
|
|
|
verbatim-cred: protocol=http
|
|
|
|
verbatim-cred: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
test_expect_success 'credential_fill stops when we get a full response' '
|
|
|
|
check fill "verbatim one two" "verbatim three four" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=one
|
|
|
|
password=two
|
|
|
|
--
|
|
|
|
verbatim: get
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: gate new fields on capability
We support the new credential and authtype fields, but we lack a way to
indicate to a credential helper that we'd like them to be used. Without
some sort of indication, the credential helper doesn't know if it should
try to provide us a username and password, or a pre-encoded credential.
For example, the helper might prefer a more restricted Bearer token if
pre-encoded credentials are possible, but might have to fall back to
more general username and password if not.
Let's provide a simple way to indicate whether Git (or, for that matter,
the helper) is capable of understanding the authtype and credential
fields. We send this capability when we generate a request, and the
other side may reply to indicate to us that it does, too.
For now, don't enable sending capabilities for the HTTP code. In a
future commit, we'll introduce appropriate handling for that code,
which requires more in-depth work.
The logic for determining whether a capability is supported may seem
complex, but it is not. At each stage, we emit the capability to the
following stage if all preceding stages have declared it. Thus, if the
caller to git credential fill didn't declare it, then we won't send it
to the helper, and if fill's caller did send but the helper doesn't
understand it, then we won't send it on in the response. If we're an
internal user, then we know about all capabilities and will request
them.
For "git credential approve" and "git credential reject", we set the
helper capability before calling the helper, since we assume that the
input we're getting from the external program comes from a previous call
to "git credential fill", and thus we'll invoke send a capability to the
helper if and only if we got one from the standard input, which is the
correct behavior.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 00:02:29 +00:00
|
|
|
test_expect_success 'credential_fill thinks a credential is a full response' '
|
|
|
|
check fill "verbatim-cred Bearer token" "verbatim three four" <<-\EOF
|
|
|
|
capability[]=authtype
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
capability[]=authtype
|
|
|
|
authtype=Bearer
|
|
|
|
credential=token
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
verbatim-cred: get
|
|
|
|
verbatim-cred: capability[]=authtype
|
|
|
|
verbatim-cred: protocol=http
|
|
|
|
verbatim-cred: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
test_expect_success 'credential_fill continues through partial response' '
|
|
|
|
check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=two
|
|
|
|
password=three
|
|
|
|
--
|
|
|
|
verbatim: get
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
verbatim: get
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
verbatim: username=one
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: new attribute password_expiry_utc
Some passwords have an expiry date known at generation. This may be
years away for a personal access token or hours for an OAuth access
token.
When multiple credential helpers are configured, `credential fill` tries
each helper in turn until it has a username and password, returning
early. If Git authentication succeeds, `credential approve`
stores the successful credential in all helpers. If authentication
fails, `credential reject` erases matching credentials in all helpers.
Helpers implement corresponding operations: get, store, erase.
The credential protocol has no expiry attribute, so helpers cannot
store expiry information. Even if a helper returned an improvised
expiry attribute, git credential discards unrecognised attributes
between operations and between helpers.
This is a particular issue when a storage helper and a
credential-generating helper are configured together:
[credential]
helper = storage # eg. cache or osxkeychain
helper = generate # eg. oauth
`credential approve` stores the generated credential in both helpers
without expiry information. Later `credential fill` may return an
expired credential from storage. There is no workaround, no matter how
clever the second helper. The user sees authentication fail (a retry
will succeed).
Introduce a password expiry attribute. In `credential fill`, ignore
expired passwords and continue to query subsequent helpers.
In the example above, `credential fill` ignores the expired password
and a fresh credential is generated. If authentication succeeds,
`credential approve` replaces the expired password in storage.
If authentication fails, the expired credential is erased by
`credential reject`. It is unnecessary but harmless for storage
helpers to self prune expired credentials.
Add support for the new attribute to credential-cache.
Eventually, I hope to see support in other popular storage helpers.
Example usage in a credential-generating helper
https://github.com/hickford/git-credential-oauth/pull/16
Signed-off-by: M Hickford <mirth.hickford@gmail.com>
Reviewed-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-02-18 06:32:57 +00:00
|
|
|
test_expect_success 'credential_fill populates password_expiry_utc' '
|
|
|
|
check fill "verbatim-with-expiry one two 9999999999" <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
username=one
|
|
|
|
password=two
|
|
|
|
password_expiry_utc=9999999999
|
|
|
|
--
|
|
|
|
verbatim-with-expiry: get
|
|
|
|
verbatim-with-expiry: protocol=http
|
|
|
|
verbatim-with-expiry: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'credential_fill ignores expired password' '
|
|
|
|
check fill "verbatim-with-expiry one two 5" "verbatim three four" <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
username=three
|
|
|
|
password=four
|
|
|
|
--
|
|
|
|
verbatim-with-expiry: get
|
|
|
|
verbatim-with-expiry: protocol=http
|
|
|
|
verbatim-with-expiry: host=example.com
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
|
|
|
verbatim: username=one
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
test_expect_success 'credential_fill passes along metadata' '
|
|
|
|
check fill "verbatim one two" <<-\EOF
|
|
|
|
protocol=ftp
|
|
|
|
host=example.com
|
|
|
|
path=foo.git
|
|
|
|
--
|
2012-06-24 11:40:00 +00:00
|
|
|
protocol=ftp
|
|
|
|
host=example.com
|
|
|
|
path=foo.git
|
2011-12-10 10:31:11 +00:00
|
|
|
username=one
|
|
|
|
password=two
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=ftp
|
|
|
|
verbatim: host=example.com
|
|
|
|
verbatim: path=foo.git
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: gate new fields on capability
We support the new credential and authtype fields, but we lack a way to
indicate to a credential helper that we'd like them to be used. Without
some sort of indication, the credential helper doesn't know if it should
try to provide us a username and password, or a pre-encoded credential.
For example, the helper might prefer a more restricted Bearer token if
pre-encoded credentials are possible, but might have to fall back to
more general username and password if not.
Let's provide a simple way to indicate whether Git (or, for that matter,
the helper) is capable of understanding the authtype and credential
fields. We send this capability when we generate a request, and the
other side may reply to indicate to us that it does, too.
For now, don't enable sending capabilities for the HTTP code. In a
future commit, we'll introduce appropriate handling for that code,
which requires more in-depth work.
The logic for determining whether a capability is supported may seem
complex, but it is not. At each stage, we emit the capability to the
following stage if all preceding stages have declared it. Thus, if the
caller to git credential fill didn't declare it, then we won't send it
to the helper, and if fill's caller did send but the helper doesn't
understand it, then we won't send it on in the response. If we're an
internal user, then we know about all capabilities and will request
them.
For "git credential approve" and "git credential reject", we set the
helper capability before calling the helper, since we assume that the
input we're getting from the external program comes from a previous call
to "git credential fill", and thus we'll invoke send a capability to the
helper if and only if we got one from the standard input, which is the
correct behavior.
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-04-17 00:02:29 +00:00
|
|
|
test_expect_success 'credential_fill produces no credential without capability' '
|
|
|
|
check fill "verbatim-cred Bearer token" <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
verbatim-cred: get
|
|
|
|
verbatim-cred: protocol=http
|
|
|
|
verbatim-cred: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
test_expect_success 'credential_approve calls all helpers' '
|
|
|
|
check approve useless "verbatim one two" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
--
|
|
|
|
useless: store
|
2020-04-19 03:47:30 +00:00
|
|
|
useless: protocol=http
|
|
|
|
useless: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
useless: username=foo
|
|
|
|
useless: password=bar
|
|
|
|
verbatim: store
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
verbatim: username=foo
|
|
|
|
verbatim: password=bar
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: new attribute password_expiry_utc
Some passwords have an expiry date known at generation. This may be
years away for a personal access token or hours for an OAuth access
token.
When multiple credential helpers are configured, `credential fill` tries
each helper in turn until it has a username and password, returning
early. If Git authentication succeeds, `credential approve`
stores the successful credential in all helpers. If authentication
fails, `credential reject` erases matching credentials in all helpers.
Helpers implement corresponding operations: get, store, erase.
The credential protocol has no expiry attribute, so helpers cannot
store expiry information. Even if a helper returned an improvised
expiry attribute, git credential discards unrecognised attributes
between operations and between helpers.
This is a particular issue when a storage helper and a
credential-generating helper are configured together:
[credential]
helper = storage # eg. cache or osxkeychain
helper = generate # eg. oauth
`credential approve` stores the generated credential in both helpers
without expiry information. Later `credential fill` may return an
expired credential from storage. There is no workaround, no matter how
clever the second helper. The user sees authentication fail (a retry
will succeed).
Introduce a password expiry attribute. In `credential fill`, ignore
expired passwords and continue to query subsequent helpers.
In the example above, `credential fill` ignores the expired password
and a fresh credential is generated. If authentication succeeds,
`credential approve` replaces the expired password in storage.
If authentication fails, the expired credential is erased by
`credential reject`. It is unnecessary but harmless for storage
helpers to self prune expired credentials.
Add support for the new attribute to credential-cache.
Eventually, I hope to see support in other popular storage helpers.
Example usage in a credential-generating helper
https://github.com/hickford/git-credential-oauth/pull/16
Signed-off-by: M Hickford <mirth.hickford@gmail.com>
Reviewed-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-02-18 06:32:57 +00:00
|
|
|
test_expect_success 'credential_approve stores password expiry' '
|
|
|
|
check approve useless <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
password_expiry_utc=9999999999
|
|
|
|
--
|
|
|
|
--
|
|
|
|
useless: store
|
|
|
|
useless: protocol=http
|
|
|
|
useless: host=example.com
|
|
|
|
useless: username=foo
|
|
|
|
useless: password=bar
|
|
|
|
useless: password_expiry_utc=9999999999
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2023-04-21 09:47:59 +00:00
|
|
|
test_expect_success 'credential_approve stores oauth refresh token' '
|
|
|
|
check approve useless <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
oauth_refresh_token=xyzzy
|
|
|
|
--
|
|
|
|
--
|
|
|
|
useless: store
|
|
|
|
useless: protocol=http
|
|
|
|
useless: host=example.com
|
|
|
|
useless: username=foo
|
|
|
|
useless: password=bar
|
|
|
|
useless: oauth_refresh_token=xyzzy
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
test_expect_success 'do not bother storing password-less credential' '
|
|
|
|
check approve useless <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=foo
|
|
|
|
--
|
|
|
|
--
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: new attribute password_expiry_utc
Some passwords have an expiry date known at generation. This may be
years away for a personal access token or hours for an OAuth access
token.
When multiple credential helpers are configured, `credential fill` tries
each helper in turn until it has a username and password, returning
early. If Git authentication succeeds, `credential approve`
stores the successful credential in all helpers. If authentication
fails, `credential reject` erases matching credentials in all helpers.
Helpers implement corresponding operations: get, store, erase.
The credential protocol has no expiry attribute, so helpers cannot
store expiry information. Even if a helper returned an improvised
expiry attribute, git credential discards unrecognised attributes
between operations and between helpers.
This is a particular issue when a storage helper and a
credential-generating helper are configured together:
[credential]
helper = storage # eg. cache or osxkeychain
helper = generate # eg. oauth
`credential approve` stores the generated credential in both helpers
without expiry information. Later `credential fill` may return an
expired credential from storage. There is no workaround, no matter how
clever the second helper. The user sees authentication fail (a retry
will succeed).
Introduce a password expiry attribute. In `credential fill`, ignore
expired passwords and continue to query subsequent helpers.
In the example above, `credential fill` ignores the expired password
and a fresh credential is generated. If authentication succeeds,
`credential approve` replaces the expired password in storage.
If authentication fails, the expired credential is erased by
`credential reject`. It is unnecessary but harmless for storage
helpers to self prune expired credentials.
Add support for the new attribute to credential-cache.
Eventually, I hope to see support in other popular storage helpers.
Example usage in a credential-generating helper
https://github.com/hickford/git-credential-oauth/pull/16
Signed-off-by: M Hickford <mirth.hickford@gmail.com>
Reviewed-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-02-18 06:32:57 +00:00
|
|
|
test_expect_success 'credential_approve does not store expired password' '
|
|
|
|
check approve useless <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
password_expiry_utc=5
|
|
|
|
--
|
|
|
|
--
|
|
|
|
EOF
|
|
|
|
'
|
2011-12-10 10:31:11 +00:00
|
|
|
|
|
|
|
test_expect_success 'credential_reject calls all helpers' '
|
|
|
|
check reject useless "verbatim one two" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
--
|
|
|
|
useless: erase
|
2020-04-19 03:47:30 +00:00
|
|
|
useless: protocol=http
|
|
|
|
useless: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
useless: username=foo
|
|
|
|
useless: password=bar
|
|
|
|
verbatim: erase
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
verbatim: username=foo
|
|
|
|
verbatim: password=bar
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: new attribute password_expiry_utc
Some passwords have an expiry date known at generation. This may be
years away for a personal access token or hours for an OAuth access
token.
When multiple credential helpers are configured, `credential fill` tries
each helper in turn until it has a username and password, returning
early. If Git authentication succeeds, `credential approve`
stores the successful credential in all helpers. If authentication
fails, `credential reject` erases matching credentials in all helpers.
Helpers implement corresponding operations: get, store, erase.
The credential protocol has no expiry attribute, so helpers cannot
store expiry information. Even if a helper returned an improvised
expiry attribute, git credential discards unrecognised attributes
between operations and between helpers.
This is a particular issue when a storage helper and a
credential-generating helper are configured together:
[credential]
helper = storage # eg. cache or osxkeychain
helper = generate # eg. oauth
`credential approve` stores the generated credential in both helpers
without expiry information. Later `credential fill` may return an
expired credential from storage. There is no workaround, no matter how
clever the second helper. The user sees authentication fail (a retry
will succeed).
Introduce a password expiry attribute. In `credential fill`, ignore
expired passwords and continue to query subsequent helpers.
In the example above, `credential fill` ignores the expired password
and a fresh credential is generated. If authentication succeeds,
`credential approve` replaces the expired password in storage.
If authentication fails, the expired credential is erased by
`credential reject`. It is unnecessary but harmless for storage
helpers to self prune expired credentials.
Add support for the new attribute to credential-cache.
Eventually, I hope to see support in other popular storage helpers.
Example usage in a credential-generating helper
https://github.com/hickford/git-credential-oauth/pull/16
Signed-off-by: M Hickford <mirth.hickford@gmail.com>
Reviewed-by: Calvin Wan <calvinwan@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-02-18 06:32:57 +00:00
|
|
|
test_expect_success 'credential_reject erases credential regardless of expiry' '
|
|
|
|
check reject useless <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
password_expiry_utc=5
|
|
|
|
--
|
|
|
|
--
|
|
|
|
useless: erase
|
|
|
|
useless: protocol=http
|
|
|
|
useless: host=example.com
|
|
|
|
useless: username=foo
|
|
|
|
useless: password=bar
|
|
|
|
useless: password_expiry_utc=5
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
test_expect_success 'usernames can be preserved' '
|
|
|
|
check fill "verbatim \"\" three" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=one
|
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=one
|
|
|
|
password=three
|
|
|
|
--
|
|
|
|
verbatim: get
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
verbatim: username=one
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'usernames can be overridden' '
|
|
|
|
check fill "verbatim two three" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=one
|
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=two
|
|
|
|
password=three
|
|
|
|
--
|
|
|
|
verbatim: get
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
verbatim: username=one
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'do not bother completing already-full credential' '
|
|
|
|
check fill "verbatim three four" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=one
|
|
|
|
password=two
|
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=one
|
|
|
|
password=two
|
|
|
|
--
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
# We can't test the basic terminal password prompt here because
|
|
|
|
# getpass() tries too hard to find the real terminal. But if our
|
|
|
|
# askpass helper is run, we know the internal getpass is working.
|
|
|
|
test_expect_success 'empty helper list falls back to internal getpass' '
|
|
|
|
check fill <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=askpass-username
|
|
|
|
password=askpass-password
|
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
askpass: Username for '\''http://example.com'\'':
|
|
|
|
askpass: Password for '\''http://askpass-username@example.com'\'':
|
2011-12-10 10:31:11 +00:00
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'internal getpass does not ask for known username' '
|
|
|
|
check fill <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=foo
|
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:11 +00:00
|
|
|
username=foo
|
|
|
|
password=askpass-password
|
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
askpass: Password for '\''http://foo@example.com'\'':
|
2011-12-10 10:31:11 +00:00
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2020-10-15 21:59:20 +00:00
|
|
|
test_expect_success 'git-credential respects core.askPass' '
|
|
|
|
write_script alternate-askpass <<-\EOF &&
|
|
|
|
echo >&2 "alternate askpass invoked"
|
|
|
|
echo alternate-value
|
|
|
|
EOF
|
|
|
|
test_config core.askpass "$PWD/alternate-askpass" &&
|
|
|
|
(
|
|
|
|
# unset GIT_ASKPASS set by lib-credential.sh which would
|
|
|
|
# override our config, but do so in a subshell so that we do
|
|
|
|
# not interfere with other tests
|
|
|
|
sane_unset GIT_ASKPASS &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
--
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
username=alternate-value
|
|
|
|
password=alternate-value
|
|
|
|
--
|
|
|
|
alternate askpass invoked
|
|
|
|
alternate askpass invoked
|
|
|
|
EOF
|
|
|
|
)
|
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:24 +00:00
|
|
|
HELPER="!f() {
|
|
|
|
cat >/dev/null
|
|
|
|
echo username=foo
|
|
|
|
echo password=bar
|
|
|
|
}; f"
|
|
|
|
test_expect_success 'respect configured credentials' '
|
|
|
|
test_config credential.helper "$HELPER" &&
|
|
|
|
check fill <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:24 +00:00
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2011-12-10 10:31:24 +00:00
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'match configured credential' '
|
|
|
|
test_config credential.https://example.com.helper "$HELPER" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
path=repo.git
|
|
|
|
--
|
2012-06-24 11:40:00 +00:00
|
|
|
protocol=https
|
|
|
|
host=example.com
|
2011-12-10 10:31:24 +00:00
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'do not match configured credential' '
|
|
|
|
test_config credential.https://foo.helper "$HELPER" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
protocol=https
|
|
|
|
host=bar
|
|
|
|
--
|
2012-06-24 11:40:00 +00:00
|
|
|
protocol=https
|
|
|
|
host=bar
|
2011-12-10 10:31:24 +00:00
|
|
|
username=askpass-username
|
|
|
|
password=askpass-password
|
|
|
|
--
|
|
|
|
askpass: Username for '\''https://bar'\'':
|
|
|
|
askpass: Password for '\''https://askpass-username@bar'\'':
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
t0300: add tests for some additional cases
There are some tricky cases in our credential helpers that we don't have
test cases for. To help prevent regressions, let's add some for these
cases:
* If there are multiple configured credential helpers, one without a
path and one with a path, we want to invoke both of them.
* If there are percent-encoded values in the URL, we handle them
properly.
* And finally, if there is a username in the remote URL, we want to
honor that over what the configuration tells us.
Finally, there's an additional case that we'd like to test for as well,
but that currently fails. In all other situations in our configuration,
we pick the last configuration setting that's provided. However, we
fail to do that for credential.username, where we pick the first setting
instead. Let's add a failing test that we have the consistent behavior
here, since that's the documented, expected behavior.
Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 02:24:11 +00:00
|
|
|
test_expect_success 'match multiple configured helpers' '
|
|
|
|
test_config credential.helper "verbatim \"\" \"\"" &&
|
|
|
|
test_config credential.https://example.com.helper "$HELPER" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
path=repo.git
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'match multiple configured helpers with URLs' '
|
|
|
|
test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
|
|
|
|
test_config credential.https://example.com.helper "$HELPER" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
path=repo.git
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'match percent-encoded values' '
|
|
|
|
test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
url=https://example.com/%2566.git
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: fix matching URLs with multiple levels in path
46fd7b3900 ("credential: allow wildcard patterns when matching config",
2020-02-20) introduced support for matching credential helpers using
urlmatch. In doing so, it introduced code to percent-encode the paths
we get from the credential helper so that they could be effectively
matched by the urlmatch code.
Unfortunately, that code had a bug: it percent-encoded the slashes in
the path, resulting in any URL path that contained multiple levels
(i.e., a directory component) not matching.
We are currently the only caller of the percent-encoding code and could
simply change it not to encode slashes. However, we still want to
encode slashes in the username component, so we need to have both
behaviors available.
So instead, let's add a flag to control encoding slashes, which is the
behavior we want here, and use it when calling the code in this case.
Add a test for credential helper URLs using multiple slashes in the
path, which our test suite previously lacked, as well as one ensuring
that we handle usernames with slashes gracefully. Since we're testing
other percent-encoding handling, let's add one for non-ASCII UTF-8
characters as well.
Reported-by: Ilya Tretyakov <it@it3xl.ru>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-27 01:18:08 +00:00
|
|
|
test_expect_success 'match percent-encoded UTF-8 values in path' '
|
|
|
|
test_config credential.https://example.com.useHttpPath true &&
|
|
|
|
test_config credential.https://example.com/perú.git.helper "$HELPER" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
url=https://example.com/per%C3%BA.git
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
path=perú.git
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'match percent-encoded values in username' '
|
|
|
|
test_config credential.https://user%2fname@example.com/foo/bar.git.helper "$HELPER" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
url=https://user%2fname@example.com/foo/bar.git
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'fetch with multiple path components' '
|
|
|
|
test_unconfig credential.helper &&
|
|
|
|
test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
url=https://example.com/foo/repo.git
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.com
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:30 +00:00
|
|
|
test_expect_success 'pull username from config' '
|
|
|
|
test_config credential.https://example.com.username foo &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
--
|
2012-06-24 11:40:00 +00:00
|
|
|
protocol=https
|
|
|
|
host=example.com
|
2011-12-10 10:31:30 +00:00
|
|
|
username=foo
|
|
|
|
password=askpass-password
|
|
|
|
--
|
|
|
|
askpass: Password for '\''https://foo@example.com'\'':
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
t0300: add tests for some additional cases
There are some tricky cases in our credential helpers that we don't have
test cases for. To help prevent regressions, let's add some for these
cases:
* If there are multiple configured credential helpers, one without a
path and one with a path, we want to invoke both of them.
* If there are percent-encoded values in the URL, we handle them
properly.
* And finally, if there is a username in the remote URL, we want to
honor that over what the configuration tells us.
Finally, there's an additional case that we'd like to test for as well,
but that currently fails. In all other situations in our configuration,
we pick the last configuration setting that's provided. However, we
fail to do that for credential.username, where we pick the first setting
instead. Let's add a failing test that we have the consistent behavior
here, since that's the documented, expected behavior.
Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 02:24:11 +00:00
|
|
|
test_expect_success 'honors username from URL over helper (URL)' '
|
|
|
|
test_config credential.https://example.com.username bob &&
|
|
|
|
test_config credential.https://example.com.helper "verbatim \"\" bar" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
url=https://alice@example.com
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=alice
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.com
|
|
|
|
verbatim: username=alice
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'honors username from URL over helper (components)' '
|
|
|
|
test_config credential.https://example.com.username bob &&
|
|
|
|
test_config credential.https://example.com.helper "verbatim \"\" bar" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=alice
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=alice
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.com
|
|
|
|
verbatim: username=alice
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: use the last matching username in the config
Everywhere else in the codebase, we use the rule that the last matching
configuration option is the one that takes effect. This is helpful
because it allows more specific configuration settings (e.g., per-repo
configuration) to override less specific settings (e.g., per-user
configuration).
However, in the credential code, we didn't honor this setting, and
instead picked the first setting we had, and stuck with it. This was
likely to ensure we picked the value from the URL, which we want to
honor over the configuration.
It's possible to do both, though, so let's check if the value is the one
we've gotten over our protocol connection, which if present will have
come from the URL, and keep it if so. Otherwise, let's overwrite the
value with the latest version we've got from the configuration, so we
keep the last configuration value.
Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 02:24:12 +00:00
|
|
|
test_expect_success 'last matching username wins' '
|
t0300: add tests for some additional cases
There are some tricky cases in our credential helpers that we don't have
test cases for. To help prevent regressions, let's add some for these
cases:
* If there are multiple configured credential helpers, one without a
path and one with a path, we want to invoke both of them.
* If there are percent-encoded values in the URL, we handle them
properly.
* And finally, if there is a username in the remote URL, we want to
honor that over what the configuration tells us.
Finally, there's an additional case that we'd like to test for as well,
but that currently fails. In all other situations in our configuration,
we pick the last configuration setting that's provided. However, we
fail to do that for credential.username, where we pick the first setting
instead. Let's add a failing test that we have the consistent behavior
here, since that's the documented, expected behavior.
Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 02:24:11 +00:00
|
|
|
test_config credential.https://example.com/path.git.username bob &&
|
|
|
|
test_config credential.https://example.com.username alice &&
|
|
|
|
test_config credential.https://example.com.helper "verbatim \"\" bar" &&
|
|
|
|
check fill <<-\EOF
|
|
|
|
url=https://example.com/path.git
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
username=alice
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.com
|
|
|
|
verbatim: username=alice
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: make relevance of http path configurable
When parsing a URL into a credential struct, we carefully
record each part of the URL, including the path on the
remote host, and use the result as part of the credential
context.
This had two practical implications:
1. Credential helpers which store a credential for later
access are likely to use the "path" portion as part of
the storage key. That means that a request to
https://example.com/foo.git
would not use the same credential that was stored in an
earlier request for:
https://example.com/bar.git
2. The prompt shown to the user includes all relevant
context, including the path.
In most cases, however, users will have a single password
per host. The behavior in (1) will be inconvenient, and the
prompt in (2) will be overly long.
This patch introduces a config option to toggle the
relevance of http paths. When turned on, we use the path as
before. When turned off, we drop the path component from the
context: helpers don't see it, and it does not appear in the
prompt.
This is nothing you couldn't do with a clever credential
helper at the start of your stack, like:
[credential "http://"]
helper = "!f() { grep -v ^path= ; }; f"
helper = your_real_helper
But doing this:
[credential]
useHttpPath = false
is way easier and more readable. Furthermore, since most
users will want the "off" behavior, that is the new default.
Users who want it "on" can set the variable (either for all
credentials, or just for a subset using
credential.*.useHttpPath).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 10:31:34 +00:00
|
|
|
test_expect_success 'http paths can be part of context' '
|
|
|
|
check fill "verbatim foo bar" <<-\EOF &&
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
path=foo.git
|
|
|
|
--
|
2012-06-24 11:40:00 +00:00
|
|
|
protocol=https
|
|
|
|
host=example.com
|
credential: make relevance of http path configurable
When parsing a URL into a credential struct, we carefully
record each part of the URL, including the path on the
remote host, and use the result as part of the credential
context.
This had two practical implications:
1. Credential helpers which store a credential for later
access are likely to use the "path" portion as part of
the storage key. That means that a request to
https://example.com/foo.git
would not use the same credential that was stored in an
earlier request for:
https://example.com/bar.git
2. The prompt shown to the user includes all relevant
context, including the path.
In most cases, however, users will have a single password
per host. The behavior in (1) will be inconvenient, and the
prompt in (2) will be overly long.
This patch introduces a config option to toggle the
relevance of http paths. When turned on, we use the path as
before. When turned off, we drop the path component from the
context: helpers don't see it, and it does not appear in the
prompt.
This is nothing you couldn't do with a clever credential
helper at the start of your stack, like:
[credential "http://"]
helper = "!f() { grep -v ^path= ; }; f"
helper = your_real_helper
But doing this:
[credential]
useHttpPath = false
is way easier and more readable. Furthermore, since most
users will want the "off" behavior, that is the new default.
Users who want it "on" can set the variable (either for all
credentials, or just for a subset using
credential.*.useHttpPath).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 10:31:34 +00:00
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.com
|
|
|
|
EOF
|
|
|
|
test_config credential.https://example.com.useHttpPath true &&
|
|
|
|
check fill "verbatim foo bar" <<-\EOF
|
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
path=foo.git
|
|
|
|
--
|
2012-06-24 11:40:00 +00:00
|
|
|
protocol=https
|
|
|
|
host=example.com
|
|
|
|
path=foo.git
|
credential: make relevance of http path configurable
When parsing a URL into a credential struct, we carefully
record each part of the URL, including the path on the
remote host, and use the result as part of the credential
context.
This had two practical implications:
1. Credential helpers which store a credential for later
access are likely to use the "path" portion as part of
the storage key. That means that a request to
https://example.com/foo.git
would not use the same credential that was stored in an
earlier request for:
https://example.com/bar.git
2. The prompt shown to the user includes all relevant
context, including the path.
In most cases, however, users will have a single password
per host. The behavior in (1) will be inconvenient, and the
prompt in (2) will be overly long.
This patch introduces a config option to toggle the
relevance of http paths. When turned on, we use the path as
before. When turned off, we drop the path component from the
context: helpers don't see it, and it does not appear in the
prompt.
This is nothing you couldn't do with a clever credential
helper at the start of your stack, like:
[credential "http://"]
helper = "!f() { grep -v ^path= ; }; f"
helper = your_real_helper
But doing this:
[credential]
useHttpPath = false
is way easier and more readable. Furthermore, since most
users will want the "off" behavior, that is the new default.
Users who want it "on" can set the variable (either for all
credentials, or just for a subset using
credential.*.useHttpPath).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-12-10 10:31:34 +00:00
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.com
|
|
|
|
verbatim: path=foo.git
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: allow wildcard patterns when matching config
In some cases, a user will want to use a specific credential helper for
a wildcard pattern, such as https://*.corp.example.com. We have code
that handles this already with the urlmatch code, so let's use that
instead of our custom code.
Since the urlmatch code is a superset of our current matching in terms
of capabilities, there shouldn't be any cases of things that matched
previously that don't match now. However, in addition to wildcard
matching, we now use partial path matching, which can cause slightly
different behavior in the case that a helper applies to the prefix
(considering path components) of the remote URL. While different, this
is probably the behavior people were wanting anyway.
Since we're using the urlmatch code, we need to encode the components
we've gotten into a URL to match, so add a function to percent-encode
data and format the URL with it. We now also no longer need to the
custom code to match URLs, so let's remove it.
Additionally, the urlmatch code always looks for the best match, whereas
we want all matches for credential helpers to preserve existing
behavior. Let's add an optional field, select_fn, that lets us control
which items we want (in this case, all of them) and default it to the
best-match code that already exists for other users.
Signed-off-by: brian m. carlson <bk2204@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-02-20 02:24:13 +00:00
|
|
|
test_expect_success 'context uses urlmatch' '
|
|
|
|
test_config "credential.https://*.org.useHttpPath" true &&
|
|
|
|
check fill "verbatim foo bar" <<-\EOF
|
|
|
|
protocol=https
|
|
|
|
host=example.org
|
|
|
|
path=foo.git
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=example.org
|
|
|
|
path=foo.git
|
|
|
|
username=foo
|
|
|
|
password=bar
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=example.org
|
|
|
|
verbatim: path=foo.git
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: let helpers tell us to quit
When we are trying to fill a credential, we loop over the
set of defined credential-helpers, then fall back to running
askpass, and then finally prompt on the terminal. Helpers
which cannot find a credential are free to tell us nothing,
but they cannot currently ask us to stop prompting.
This patch lets them provide a "quit" attribute, which asks
us to stop the process entirely (avoiding running more
helpers, as well as the askpass/terminal prompt).
This has a few possible uses:
1. A helper which prompts the user itself (e.g., in a
dialog) can provide a "cancel" button to the user to
stop further prompts.
2. Some helpers may know that prompting cannot possibly
work. For example, if their role is to broker a ticket
from an external auth system and that auth system
cannot be contacted, there is no point in continuing
(we need a ticket to authenticate, and the user cannot
provide one by typing it in).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-12-04 03:46:48 +00:00
|
|
|
test_expect_success 'helpers can abort the process' '
|
|
|
|
test_must_fail git \
|
2020-04-19 05:36:02 +00:00
|
|
|
-c credential.helper=quit \
|
credential: let helpers tell us to quit
When we are trying to fill a credential, we loop over the
set of defined credential-helpers, then fall back to running
askpass, and then finally prompt on the terminal. Helpers
which cannot find a credential are free to tell us nothing,
but they cannot currently ask us to stop prompting.
This patch lets them provide a "quit" attribute, which asks
us to stop the process entirely (avoiding running more
helpers, as well as the askpass/terminal prompt).
This has a few possible uses:
1. A helper which prompts the user itself (e.g., in a
dialog) can provide a "cancel" button to the user to
stop further prompts.
2. Some helpers may know that prompting cannot possibly
work. For example, if their role is to broker a ticket
from an external auth system and that auth system
cannot be contacted, there is no point in continuing
(we need a ticket to authenticate, and the user cannot
provide one by typing it in).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-12-04 03:46:48 +00:00
|
|
|
-c credential.helper="verbatim foo bar" \
|
2020-04-19 03:47:30 +00:00
|
|
|
credential fill >stdout 2>stderr <<-\EOF &&
|
|
|
|
protocol=http
|
|
|
|
host=example.com
|
|
|
|
EOF
|
2020-04-19 23:26:41 +00:00
|
|
|
test_must_be_empty stdout &&
|
2020-04-19 05:36:02 +00:00
|
|
|
cat >expect <<-\EOF &&
|
|
|
|
quit: get
|
2020-04-19 03:47:30 +00:00
|
|
|
quit: protocol=http
|
|
|
|
quit: host=example.com
|
2020-04-19 05:36:02 +00:00
|
|
|
fatal: credential helper '\''quit'\'' told us to quit
|
|
|
|
EOF
|
2021-02-11 01:53:53 +00:00
|
|
|
test_cmp expect stderr
|
credential: let helpers tell us to quit
When we are trying to fill a credential, we loop over the
set of defined credential-helpers, then fall back to running
askpass, and then finally prompt on the terminal. Helpers
which cannot find a credential are free to tell us nothing,
but they cannot currently ask us to stop prompting.
This patch lets them provide a "quit" attribute, which asks
us to stop the process entirely (avoiding running more
helpers, as well as the askpass/terminal prompt).
This has a few possible uses:
1. A helper which prompts the user itself (e.g., in a
dialog) can provide a "cancel" button to the user to
stop further prompts.
2. Some helpers may know that prompting cannot possibly
work. For example, if their role is to broker a ticket
from an external auth system and that auth system
cannot be contacted, there is no point in continuing
(we need a ticket to authenticate, and the user cannot
provide one by typing it in).
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-12-04 03:46:48 +00:00
|
|
|
'
|
|
|
|
|
2016-02-26 10:51:35 +00:00
|
|
|
test_expect_success 'empty helper spec resets helper list' '
|
|
|
|
test_config credential.helper "verbatim file file" &&
|
|
|
|
check fill "" "verbatim cmdline cmdline" <<-\EOF
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2016-02-26 10:51:35 +00:00
|
|
|
--
|
2020-04-19 03:47:30 +00:00
|
|
|
protocol=http
|
|
|
|
host=example.com
|
2016-02-26 10:51:35 +00:00
|
|
|
username=cmdline
|
|
|
|
password=cmdline
|
|
|
|
--
|
|
|
|
verbatim: get
|
2020-04-19 03:47:30 +00:00
|
|
|
verbatim: protocol=http
|
|
|
|
verbatim: host=example.com
|
2016-02-26 10:51:35 +00:00
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: refuse to operate when missing host or protocol
The credential helper protocol was designed to be very flexible: the
fields it takes as input are treated as a pattern, and any missing
fields are taken as wildcards. This allows unusual things like:
echo protocol=https | git credential reject
to delete all stored https credentials (assuming the helpers themselves
treat the input that way). But when helpers are invoked automatically by
Git, this flexibility works against us. If for whatever reason we don't
have a "host" field, then we'd match _any_ host. When you're filling a
credential to send to a remote server, this is almost certainly not what
you want.
Prevent this at the layer that writes to the credential helper. Add a
check to the credential API that the host and protocol are always passed
in, and add an assertion to the credential_write function that speaks
credential helper protocol to be doubly sure.
There are a few ways this can be triggered in practice:
- the "git credential" command passes along arbitrary credential
parameters it reads from stdin.
- until the previous patch, when the host field of a URL is empty, we
would leave it unset (rather than setting it to the empty string)
- a URL like "example.com/foo.git" is treated by curl as if "http://"
was present, but our parser sees it as a non-URL and leaves all
fields unset
- the recent fix for URLs with embedded newlines blanks the URL but
otherwise continues. Rather than having the desired effect of
looking up no credential at all, many helpers will return _any_
credential
Our earlier test for an embedded newline didn't catch this because it
only checked that the credential was cleared, but didn't configure an
actual helper. Configuring the "verbatim" helper in the test would show
that it is invoked (it's obviously a silly helper which doesn't look at
its input, but the point is that it shouldn't be run at all). Since
we're switching this case to die(), we don't need to bother with a
helper. We can see the new behavior just by checking that the operation
fails.
We'll add new tests covering partial input as well (these can be
triggered through various means with url-parsing, but it's simpler to
just check them directly, as we know we are covered even if the url
parser changes behavior in the future).
[jn: changed to die() instead of logging and showing a manual
username/password prompt]
Reported-by: Carlo Arenas <carenas@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
2020-04-19 03:50:48 +00:00
|
|
|
test_expect_success 'url parser rejects embedded newlines' '
|
|
|
|
test_must_fail git credential fill 2>stderr <<-\EOF &&
|
2020-03-11 21:53:41 +00:00
|
|
|
url=https://one.example.com?%0ahost=two.example.com/
|
credential: refuse to operate when missing host or protocol
The credential helper protocol was designed to be very flexible: the
fields it takes as input are treated as a pattern, and any missing
fields are taken as wildcards. This allows unusual things like:
echo protocol=https | git credential reject
to delete all stored https credentials (assuming the helpers themselves
treat the input that way). But when helpers are invoked automatically by
Git, this flexibility works against us. If for whatever reason we don't
have a "host" field, then we'd match _any_ host. When you're filling a
credential to send to a remote server, this is almost certainly not what
you want.
Prevent this at the layer that writes to the credential helper. Add a
check to the credential API that the host and protocol are always passed
in, and add an assertion to the credential_write function that speaks
credential helper protocol to be doubly sure.
There are a few ways this can be triggered in practice:
- the "git credential" command passes along arbitrary credential
parameters it reads from stdin.
- until the previous patch, when the host field of a URL is empty, we
would leave it unset (rather than setting it to the empty string)
- a URL like "example.com/foo.git" is treated by curl as if "http://"
was present, but our parser sees it as a non-URL and leaves all
fields unset
- the recent fix for URLs with embedded newlines blanks the URL but
otherwise continues. Rather than having the desired effect of
looking up no credential at all, many helpers will return _any_
credential
Our earlier test for an embedded newline didn't catch this because it
only checked that the credential was cleared, but didn't configure an
actual helper. Configuring the "verbatim" helper in the test would show
that it is invoked (it's obviously a silly helper which doesn't look at
its input, but the point is that it shouldn't be run at all). Since
we're switching this case to die(), we don't need to bother with a
helper. We can see the new behavior just by checking that the operation
fails.
We'll add new tests covering partial input as well (these can be
triggered through various means with url-parsing, but it's simpler to
just check them directly, as we know we are covered even if the url
parser changes behavior in the future).
[jn: changed to die() instead of logging and showing a manual
username/password prompt]
Reported-by: Carlo Arenas <carenas@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
2020-04-19 03:50:48 +00:00
|
|
|
EOF
|
|
|
|
cat >expect <<-\EOF &&
|
2020-04-22 20:43:01 +00:00
|
|
|
warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
|
credential: die() when parsing invalid urls
When we try to initialize credential loading by URL and find that the
URL is invalid, we set all fields to NULL in order to avoid acting on
malicious input. Later when we request credentials, we diagonse the
erroneous input:
fatal: refusing to work with credential missing host field
This is problematic in two ways:
- The message doesn't tell the user *why* we are missing the host
field, so they can't tell from this message alone how to recover.
There can be intervening messages after the original warning of
bad input, so the user may not have the context to put two and two
together.
- The error only occurs when we actually need to get a credential. If
the URL permits anonymous access, the only encouragement the user gets
to correct their bogus URL is a quiet warning.
This is inconsistent with the check we perform in fsck, where any use
of such a URL as a submodule is an error.
When we see such a bogus URL, let's not try to be nice and continue
without helpers. Instead, die() immediately. This is simpler and
obviously safe. And there's very little chance of disrupting a normal
workflow.
It's _possible_ that somebody has a legitimate URL with a raw newline in
it. It already wouldn't work with credential helpers, so this patch
steps that up from an inconvenience to "we will refuse to work with it
at all". If such a case does exist, we should figure out a way to work
with it (especially if the newline is only in the path component, which
we normally don't even pass to helpers). But until we see a real report,
we're better off being defensive.
Reported-by: Carlo Arenas <carenas@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
2020-04-19 03:53:09 +00:00
|
|
|
fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
|
2020-03-11 21:53:41 +00:00
|
|
|
EOF
|
2021-02-11 01:53:53 +00:00
|
|
|
test_cmp expect stderr
|
2020-03-11 21:53:41 +00:00
|
|
|
'
|
|
|
|
|
2020-04-19 03:48:05 +00:00
|
|
|
test_expect_success 'host-less URLs are parsed as empty host' '
|
|
|
|
check fill "verbatim foo bar" <<-\EOF
|
|
|
|
url=cert:///path/to/cert.pem
|
2020-03-12 05:31:11 +00:00
|
|
|
--
|
2020-04-19 03:48:05 +00:00
|
|
|
protocol=cert
|
|
|
|
host=
|
|
|
|
path=path/to/cert.pem
|
|
|
|
username=foo
|
|
|
|
password=bar
|
2020-03-12 05:31:11 +00:00
|
|
|
--
|
2020-04-19 03:48:05 +00:00
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=cert
|
|
|
|
verbatim: host=
|
|
|
|
verbatim: path=path/to/cert.pem
|
|
|
|
EOF
|
|
|
|
'
|
|
|
|
|
credential: refuse to operate when missing host or protocol
The credential helper protocol was designed to be very flexible: the
fields it takes as input are treated as a pattern, and any missing
fields are taken as wildcards. This allows unusual things like:
echo protocol=https | git credential reject
to delete all stored https credentials (assuming the helpers themselves
treat the input that way). But when helpers are invoked automatically by
Git, this flexibility works against us. If for whatever reason we don't
have a "host" field, then we'd match _any_ host. When you're filling a
credential to send to a remote server, this is almost certainly not what
you want.
Prevent this at the layer that writes to the credential helper. Add a
check to the credential API that the host and protocol are always passed
in, and add an assertion to the credential_write function that speaks
credential helper protocol to be doubly sure.
There are a few ways this can be triggered in practice:
- the "git credential" command passes along arbitrary credential
parameters it reads from stdin.
- until the previous patch, when the host field of a URL is empty, we
would leave it unset (rather than setting it to the empty string)
- a URL like "example.com/foo.git" is treated by curl as if "http://"
was present, but our parser sees it as a non-URL and leaves all
fields unset
- the recent fix for URLs with embedded newlines blanks the URL but
otherwise continues. Rather than having the desired effect of
looking up no credential at all, many helpers will return _any_
credential
Our earlier test for an embedded newline didn't catch this because it
only checked that the credential was cleared, but didn't configure an
actual helper. Configuring the "verbatim" helper in the test would show
that it is invoked (it's obviously a silly helper which doesn't look at
its input, but the point is that it shouldn't be run at all). Since
we're switching this case to die(), we don't need to bother with a
helper. We can see the new behavior just by checking that the operation
fails.
We'll add new tests covering partial input as well (these can be
triggered through various means with url-parsing, but it's simpler to
just check them directly, as we know we are covered even if the url
parser changes behavior in the future).
[jn: changed to die() instead of logging and showing a manual
username/password prompt]
Reported-by: Carlo Arenas <carenas@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
2020-04-19 03:50:48 +00:00
|
|
|
test_expect_success 'credential system refuses to work with missing host' '
|
|
|
|
test_must_fail git credential fill 2>stderr <<-\EOF &&
|
|
|
|
protocol=http
|
|
|
|
EOF
|
|
|
|
cat >expect <<-\EOF &&
|
|
|
|
fatal: refusing to work with credential missing host field
|
|
|
|
EOF
|
2021-02-11 01:53:53 +00:00
|
|
|
test_cmp expect stderr
|
credential: refuse to operate when missing host or protocol
The credential helper protocol was designed to be very flexible: the
fields it takes as input are treated as a pattern, and any missing
fields are taken as wildcards. This allows unusual things like:
echo protocol=https | git credential reject
to delete all stored https credentials (assuming the helpers themselves
treat the input that way). But when helpers are invoked automatically by
Git, this flexibility works against us. If for whatever reason we don't
have a "host" field, then we'd match _any_ host. When you're filling a
credential to send to a remote server, this is almost certainly not what
you want.
Prevent this at the layer that writes to the credential helper. Add a
check to the credential API that the host and protocol are always passed
in, and add an assertion to the credential_write function that speaks
credential helper protocol to be doubly sure.
There are a few ways this can be triggered in practice:
- the "git credential" command passes along arbitrary credential
parameters it reads from stdin.
- until the previous patch, when the host field of a URL is empty, we
would leave it unset (rather than setting it to the empty string)
- a URL like "example.com/foo.git" is treated by curl as if "http://"
was present, but our parser sees it as a non-URL and leaves all
fields unset
- the recent fix for URLs with embedded newlines blanks the URL but
otherwise continues. Rather than having the desired effect of
looking up no credential at all, many helpers will return _any_
credential
Our earlier test for an embedded newline didn't catch this because it
only checked that the credential was cleared, but didn't configure an
actual helper. Configuring the "verbatim" helper in the test would show
that it is invoked (it's obviously a silly helper which doesn't look at
its input, but the point is that it shouldn't be run at all). Since
we're switching this case to die(), we don't need to bother with a
helper. We can see the new behavior just by checking that the operation
fails.
We'll add new tests covering partial input as well (these can be
triggered through various means with url-parsing, but it's simpler to
just check them directly, as we know we are covered even if the url
parser changes behavior in the future).
[jn: changed to die() instead of logging and showing a manual
username/password prompt]
Reported-by: Carlo Arenas <carenas@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
2020-04-19 03:50:48 +00:00
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'credential system refuses to work with missing protocol' '
|
|
|
|
test_must_fail git credential fill 2>stderr <<-\EOF &&
|
|
|
|
host=example.com
|
|
|
|
EOF
|
|
|
|
cat >expect <<-\EOF &&
|
|
|
|
fatal: refusing to work with credential missing protocol field
|
2020-03-11 21:53:41 +00:00
|
|
|
EOF
|
2021-02-11 01:53:53 +00:00
|
|
|
test_cmp expect stderr
|
2020-03-11 21:53:41 +00:00
|
|
|
'
|
|
|
|
|
2020-04-14 21:43:04 +00:00
|
|
|
# usage: check_host_and_path <url> <expected-host> <expected-path>
|
|
|
|
check_host_and_path () {
|
|
|
|
# we always parse the path component, but we need this to make sure it
|
|
|
|
# is passed to the helper
|
|
|
|
test_config credential.useHTTPPath true &&
|
|
|
|
check fill "verbatim user pass" <<-EOF
|
|
|
|
url=$1
|
|
|
|
--
|
|
|
|
protocol=https
|
|
|
|
host=$2
|
|
|
|
path=$3
|
|
|
|
username=user
|
|
|
|
password=pass
|
|
|
|
--
|
|
|
|
verbatim: get
|
|
|
|
verbatim: protocol=https
|
|
|
|
verbatim: host=$2
|
|
|
|
verbatim: path=$3
|
|
|
|
EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
test_expect_success 'url parser handles bare query marker' '
|
|
|
|
check_host_and_path https://example.com?foo.git example.com ?foo.git
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'url parser handles bare fragment marker' '
|
|
|
|
check_host_and_path https://example.com#foo.git example.com "#foo.git"
|
|
|
|
'
|
|
|
|
|
|
|
|
test_expect_success 'url parser not confused by encoded markers' '
|
|
|
|
check_host_and_path https://example.com%23%3f%2f/foo.git \
|
|
|
|
"example.com#?/" foo.git
|
|
|
|
'
|
|
|
|
|
2020-04-24 11:49:52 +00:00
|
|
|
test_expect_success 'credential config with partial URLs' '
|
|
|
|
echo "echo password=yep" | write_script git-credential-yep &&
|
|
|
|
test_write_lines url=https://user@example.com/repo.git >stdin &&
|
|
|
|
for partial in \
|
|
|
|
example.com \
|
|
|
|
user@example.com \
|
|
|
|
https:// \
|
|
|
|
https://example.com \
|
|
|
|
https://example.com/ \
|
|
|
|
https://user@example.com \
|
|
|
|
https://user@example.com/ \
|
|
|
|
https://example.com/repo.git \
|
|
|
|
https://user@example.com/repo.git \
|
|
|
|
/repo.git
|
|
|
|
do
|
|
|
|
git -c credential.$partial.helper=yep \
|
|
|
|
credential fill <stdin >stdout &&
|
|
|
|
grep yep stdout ||
|
|
|
|
return 1
|
|
|
|
done &&
|
|
|
|
|
|
|
|
for partial in \
|
|
|
|
dont.use.this \
|
|
|
|
http:// \
|
|
|
|
/repo
|
|
|
|
do
|
|
|
|
git -c credential.$partial.helper=yep \
|
|
|
|
credential fill <stdin >stdout &&
|
|
|
|
! grep yep stdout ||
|
|
|
|
return 1
|
|
|
|
done &&
|
|
|
|
|
|
|
|
git -c credential.$partial.helper=yep \
|
|
|
|
-c credential.with%0anewline.username=uh-oh \
|
2023-04-17 19:10:39 +00:00
|
|
|
credential fill <stdin 2>stderr &&
|
2023-10-31 05:23:30 +00:00
|
|
|
test_grep "skipping credential lookup for key" stderr
|
2020-04-24 11:49:52 +00:00
|
|
|
'
|
|
|
|
|
2011-12-10 10:31:11 +00:00
|
|
|
test_done
|