credential: add WWW-Authenticate header to cred requests
Add the value of the WWW-Authenticate response header to credential requests. Credential helpers that understand and support HTTP authentication and authorization can use this standard header (RFC 2616 Section 14.47 [1]) to generate valid credentials. WWW-Authenticate headers can contain information pertaining to the authority, authentication mechanism, or extra parameters/scopes that are required. The current I/O format for credential helpers only allows for unique names for properties/attributes, so in order to transmit multiple header values (with a specific order) we introduce a new convention whereby a C-style array syntax is used in the property name to denote multiple ordered values for the same property. In this case we send multiple `wwwauth[]` properties where the order that the repeated attributes appear in the conversation reflects the order that the WWW-Authenticate headers appeared in the HTTP response. Add a set of tests to exercise the HTTP authentication header parsing and the interop with credential helpers. Credential helpers will receive WWW-Authenticate information in credential requests. [1] https://datatracker.ietf.org/doc/html/rfc2616#section-14.47 Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
6b8dda9a4f
commit
5f2117b24f
@ -113,7 +113,13 @@ separated by an `=` (equals) sign, followed by a newline.
|
|||||||
The key may contain any bytes except `=`, newline, or NUL. The value may
|
The key may contain any bytes except `=`, newline, or NUL. The value may
|
||||||
contain any bytes except newline or NUL.
|
contain any bytes except newline or NUL.
|
||||||
|
|
||||||
In both cases, all bytes are treated as-is (i.e., there is no quoting,
|
Attributes with keys that end with C-style array brackets `[]` can have
|
||||||
|
multiple values. Each instance of a multi-valued attribute forms an
|
||||||
|
ordered list of values - the order of the repeated attributes defines
|
||||||
|
the order of the values. An empty multi-valued attribute (`key[]=\n`)
|
||||||
|
acts to clear any previous entries and reset the list.
|
||||||
|
|
||||||
|
In all cases, all bytes are treated as-is (i.e., there is no quoting,
|
||||||
and one cannot transmit a value with newline or NUL in it). The list of
|
and one cannot transmit a value with newline or NUL in it). The list of
|
||||||
attributes is terminated by a blank line or end-of-file.
|
attributes is terminated by a blank line or end-of-file.
|
||||||
|
|
||||||
@ -160,6 +166,17 @@ empty string.
|
|||||||
Components which are missing from the URL (e.g., there is no
|
Components which are missing from the URL (e.g., there is no
|
||||||
username in the example above) will be left unset.
|
username in the example above) will be left unset.
|
||||||
|
|
||||||
|
`wwwauth[]`::
|
||||||
|
|
||||||
|
When an HTTP response is received by Git that includes one or more
|
||||||
|
'WWW-Authenticate' authentication headers, these will be passed by Git
|
||||||
|
to credential helpers.
|
||||||
|
+
|
||||||
|
Each 'WWW-Authenticate' header value is passed as a multi-valued
|
||||||
|
attribute 'wwwauth[]', where the order of the attributes is the same as
|
||||||
|
they appear in the HTTP response. This attribute is 'one-way' from Git
|
||||||
|
to pass additional information to credential helpers.
|
||||||
|
|
||||||
Unrecognised attributes are silently discarded.
|
Unrecognised attributes are silently discarded.
|
||||||
|
|
||||||
GIT
|
GIT
|
||||||
|
@ -270,6 +270,9 @@ void credential_write(const struct credential *c, FILE *fp)
|
|||||||
credential_write_item(fp, "path", c->path, 0);
|
credential_write_item(fp, "path", c->path, 0);
|
||||||
credential_write_item(fp, "username", c->username, 0);
|
credential_write_item(fp, "username", c->username, 0);
|
||||||
credential_write_item(fp, "password", c->password, 0);
|
credential_write_item(fp, "password", c->password, 0);
|
||||||
|
for (size_t i = 0; i < c->wwwauth_headers.nr; i++)
|
||||||
|
credential_write_item(fp, "wwwauth[]", c->wwwauth_headers.v[i],
|
||||||
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int run_credential_helper(struct credential *c,
|
static int run_credential_helper(struct credential *c,
|
||||||
|
@ -70,6 +70,248 @@ test_expect_success 'access using basic auth' '
|
|||||||
expect_credential_query get <<-EOF &&
|
expect_credential_query get <<-EOF &&
|
||||||
protocol=http
|
protocol=http
|
||||||
host=$HTTPD_DEST
|
host=$HTTPD_DEST
|
||||||
|
wwwauth[]=Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query store <<-EOF
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using basic auth invalid credentials' '
|
||||||
|
test_when_finished "per_test_cleanup" &&
|
||||||
|
|
||||||
|
set_credential_reply get <<-EOF &&
|
||||||
|
username=baduser
|
||||||
|
password=wrong-passwd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Basic base64(alice:secret-passwd)
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
|
||||||
|
Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
|
||||||
|
WWW-Authenticate: Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
test_must_fail git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
wwwauth[]=Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query erase <<-EOF
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
username=baduser
|
||||||
|
password=wrong-passwd
|
||||||
|
wwwauth[]=Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using basic auth with extra challenges' '
|
||||||
|
test_when_finished "per_test_cleanup" &&
|
||||||
|
|
||||||
|
set_credential_reply get <<-EOF &&
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Basic base64(alice:secret-passwd)
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
|
||||||
|
Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
|
||||||
|
WWW-Authenticate: FooBar param1="value1" param2="value2"
|
||||||
|
WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
|
||||||
|
WWW-Authenticate: Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
wwwauth[]=FooBar param1="value1" param2="value2"
|
||||||
|
wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
|
||||||
|
wwwauth[]=Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query store <<-EOF
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using basic auth mixed-case wwwauth header name' '
|
||||||
|
test_when_finished "per_test_cleanup" &&
|
||||||
|
|
||||||
|
set_credential_reply get <<-EOF &&
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Basic base64(alice:secret-passwd)
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
|
||||||
|
Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
|
||||||
|
www-authenticate: foobar param1="value1" param2="value2"
|
||||||
|
WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
|
||||||
|
WwW-aUtHeNtIcAtE: baSiC realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
wwwauth[]=foobar param1="value1" param2="value2"
|
||||||
|
wwwauth[]=BEARER authorize_uri="id.example.com" p=1 q=0
|
||||||
|
wwwauth[]=baSiC realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query store <<-EOF
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using basic auth with wwwauth header continuations' '
|
||||||
|
test_when_finished "per_test_cleanup" &&
|
||||||
|
|
||||||
|
set_credential_reply get <<-EOF &&
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Basic base64(alice:secret-passwd)
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
|
||||||
|
Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Note that leading and trailing whitespace is important to correctly
|
||||||
|
# simulate a continuation/folded header.
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
|
||||||
|
WWW-Authenticate: FooBar param1="value1"
|
||||||
|
param2="value2"
|
||||||
|
WWW-Authenticate: Bearer authorize_uri="id.example.com"
|
||||||
|
p=1
|
||||||
|
q=0
|
||||||
|
WWW-Authenticate: Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
wwwauth[]=FooBar param1="value1" param2="value2"
|
||||||
|
wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
|
||||||
|
wwwauth[]=Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query store <<-EOF
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using basic auth with wwwauth header empty continuations' '
|
||||||
|
test_when_finished "per_test_cleanup" &&
|
||||||
|
|
||||||
|
set_credential_reply get <<-EOF &&
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Basic base64(alice:secret-passwd)
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
|
||||||
|
Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
|
||||||
|
EOF
|
||||||
|
|
||||||
|
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
|
||||||
|
|
||||||
|
# Note that leading and trailing whitespace is important to correctly
|
||||||
|
# simulate a continuation/folded header.
|
||||||
|
printf "">$CHALLENGE &&
|
||||||
|
printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >$CHALLENGE &&
|
||||||
|
printf " \r\n" >>$CHALLENGE &&
|
||||||
|
printf " param2=\"value2\"\r\n" >>$CHALLENGE &&
|
||||||
|
printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>$CHALLENGE &&
|
||||||
|
printf " p=1\r\n" >>$CHALLENGE &&
|
||||||
|
printf " \r\n" >>$CHALLENGE &&
|
||||||
|
printf " q=0\r\n" >>$CHALLENGE &&
|
||||||
|
printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>$CHALLENGE &&
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
wwwauth[]=FooBar param1="value1" param2="value2"
|
||||||
|
wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
|
||||||
|
wwwauth[]=Basic realm="example.com"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
expect_credential_query store <<-EOF
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'access using basic auth with wwwauth header mixed line-endings' '
|
||||||
|
test_when_finished "per_test_cleanup" &&
|
||||||
|
|
||||||
|
set_credential_reply get <<-EOF &&
|
||||||
|
username=alice
|
||||||
|
password=secret-passwd
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Basic base64(alice:secret-passwd)
|
||||||
|
cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
|
||||||
|
Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
|
||||||
|
EOF
|
||||||
|
|
||||||
|
CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
|
||||||
|
|
||||||
|
# Note that leading and trailing whitespace is important to correctly
|
||||||
|
# simulate a continuation/folded header.
|
||||||
|
printf "">$CHALLENGE &&
|
||||||
|
printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >$CHALLENGE &&
|
||||||
|
printf " \r\n" >>$CHALLENGE &&
|
||||||
|
printf "\tparam2=\"value2\"\r\n" >>$CHALLENGE &&
|
||||||
|
printf "WWW-Authenticate: Basic realm=\"example.com\"" >>$CHALLENGE &&
|
||||||
|
|
||||||
|
test_config_global credential.helper test-helper &&
|
||||||
|
git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
|
||||||
|
|
||||||
|
expect_credential_query get <<-EOF &&
|
||||||
|
protocol=http
|
||||||
|
host=$HTTPD_DEST
|
||||||
|
wwwauth[]=FooBar param1="value1" param2="value2"
|
||||||
|
wwwauth[]=Basic realm="example.com"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
expect_credential_query store <<-EOF
|
expect_credential_query store <<-EOF
|
||||||
|
Loading…
Reference in New Issue
Block a user