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
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
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, "username", c->username, 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,
|
||||
|
@ -70,6 +70,248 @@ test_expect_success 'access using basic auth' '
|
||||
expect_credential_query get <<-EOF &&
|
||||
protocol=http
|
||||
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
|
||||
|
||||
expect_credential_query store <<-EOF
|
||||
|
Loading…
Reference in New Issue
Block a user