t5563: refactor for multi-stage authentication

Some HTTP authentication schemes, such as NTLM- and Kerberos-based options, require more than one round trip to authenticate. Currently, these can only be supported in libcurl, since Git does not have support for this in the credential helper protocol. However, in a future commit, we'll add support for this functionality into the credential helper protocol and Git itself. Because we don't really want to implement either NTLM or Kerberos, both of which are complex protocols, we'll want to test this using a fake credential authentication scheme. In order to do so, update t5563 and its backend to allow us to accept multiple sets of credentials and respond with different behavior in each case. Since we can now provide any number of possible status codes, provide a non-specific reason phrase so we don't have to generate a more specific one based on the response. The reason phrase is mandatory according to the status-line production in RFC 7230, but clients SHOULD ignore it, and curl does (except to print it). Each entry in the authorization and challenge fields contains an ID, which indicates a corresponding credential and response. If the response is a 200 status, then we continue to execute git-http-backend. Otherwise, we print the corresponding status and response. If no ID is matched, we use the default response with a status of 401. Note that there is an implicit order to the parameters. The ID is always first and the creds or response value is always last, and therefore may contain spaces, equals signs, or other arbitrary data. Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

brian m. carlson committed Apr 17, 2024 at 00:02 UTC 37417b771707786756e94a589441e6510e9f57e4
2 files changed +66 -47
t/lib-httpd/nph-custom-auth.sh
+13 -4
@@ -19,21 +19,30 @@ CHALLENGE_FILE=custom-auth.challenge
19 #
20
21 if test -n "$HTTP_AUTHORIZATION" && \
22 - grep -Fqsx "${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
22 + grep -Fqs "creds=${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE"
23 then
24 + idno=$(grep -F "creds=${HTTP_AUTHORIZATION}" "$VALID_CREDS_FILE" | sed -e 's/^id=\([a-z0-9-][a-z0-9-]*\) .*$/\1/')
25 + status=$(sed -ne "s/^id=$idno.*status=\\([0-9][0-9][0-9]\\).*\$/\\1/p" "$CHALLENGE_FILE" | head -n1)
26 # Note that although git-http-backend returns a status line, it
27 # does so using a CGI 'Status' header. Because this script is an
28 # No Parsed Headers (NPH) script, we must return a real HTTP
29 # status line.
30 # This is only a test script, so we don't bother to check for
31 # the actual status from git-http-backend and always return 200.
30 - echo 'HTTP/1.1 200 OK'
31 - exec "$GIT_EXEC_PATH"/git-http-backend
32 + echo "HTTP/1.1 $status Nonspecific Reason Phrase"
33 + if test "$status" -eq 200
34 + then
35 + exec "$GIT_EXEC_PATH"/git-http-backend
36 + else
37 + sed -ne "s/^id=$idno.*response=//p" "$CHALLENGE_FILE"
38 + echo
39 + exit
40 + fi
41 fi
42
43 echo 'HTTP/1.1 401 Authorization Required'
44 if test -f "$CHALLENGE_FILE"
45 then
37 - cat "$CHALLENGE_FILE"
46 + sed -ne 's/^id=default.*response=//p' "$CHALLENGE_FILE"
47 fi
48 echo
t/t5563-simple-http-auth.sh
+53 -43
@@ -63,11 +63,12 @@ test_expect_success 'access using basic auth' '
63
64 # Basic base64(alice:secret-passwd)
65 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
66 - Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
66 + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
67 EOF
68
69 cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
70 - WWW-Authenticate: Basic realm="example.com"
70 + id=1 status=200
71 + id=default response=WWW-Authenticate: Basic realm="example.com"
72 EOF
73
74 test_config_global credential.helper test-helper &&
@@ -100,11 +101,12 @@ test_expect_success 'access using basic auth via authtype' '
101
102 # Basic base64(alice:secret-passwd)
103 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
103 - Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
104 + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
105 EOF
106
107 cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
107 - WWW-Authenticate: Basic realm="example.com"
108 + id=1 status=200
109 + id=default response=WWW-Authenticate: Basic realm="example.com"
110 EOF
111
112 test_config_global credential.helper test-helper &&
@@ -137,11 +139,12 @@ test_expect_success 'access using basic auth invalid credentials' '
139
140 # Basic base64(alice:secret-passwd)
141 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
140 - Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
142 + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
143 EOF
144
145 cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
144 - WWW-Authenticate: Basic realm="example.com"
146 + id=1 status=200
147 + id=default response=WWW-Authenticate: Basic realm="example.com"
148 EOF
149
150 test_config_global credential.helper test-helper &&
@@ -174,13 +177,14 @@ test_expect_success 'access using basic auth with extra challenges' '
177
178 # Basic base64(alice:secret-passwd)
179 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
177 - Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
180 + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
181 EOF
182
183 cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
181 - WWW-Authenticate: FooBar param1="value1" param2="value2"
182 - WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
183 - WWW-Authenticate: Basic realm="example.com"
184 + id=1 status=200
185 + id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
186 + id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
187 + id=default response=WWW-Authenticate: Basic realm="example.com"
188 EOF
189
190 test_config_global credential.helper test-helper &&
@@ -214,13 +218,14 @@ test_expect_success 'access using basic auth mixed-case wwwauth header name' '
218
219 # Basic base64(alice:secret-passwd)
220 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
217 - Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
221 + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
222 EOF
223
224 cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
221 - www-authenticate: foobar param1="value1" param2="value2"
222 - WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
223 - WwW-aUtHeNtIcAtE: baSiC realm="example.com"
225 + id=1 status=200
226 + id=default response=www-authenticate: foobar param1="value1" param2="value2"
227 + id=default response=WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
228 + id=default response=WwW-aUtHeNtIcAtE: baSiC realm="example.com"
229 EOF
230
231 test_config_global credential.helper test-helper &&
@@ -254,18 +259,19 @@ test_expect_success 'access using basic auth with wwwauth header continuations'
259
260 # Basic base64(alice:secret-passwd)
261 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
257 - Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
262 + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
263 EOF
264
265 # Note that leading and trailing whitespace is important to correctly
266 # simulate a continuation/folded header.
267 cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
263 - WWW-Authenticate: FooBar param1="value1"
264 - param2="value2"
265 - WWW-Authenticate: Bearer authorize_uri="id.example.com"
266 - p=1
267 - q=0
268 - WWW-Authenticate: Basic realm="example.com"
268 + id=1 status=200
269 + id=default response=WWW-Authenticate: FooBar param1="value1"
270 + id=default response= param2="value2"
271 + id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com"
272 + id=default response= p=1
273 + id=default response= q=0
274 + id=default response=WWW-Authenticate: Basic realm="example.com"
275 EOF
276
277 test_config_global credential.helper test-helper &&
@@ -299,21 +305,22 @@ test_expect_success 'access using basic auth with wwwauth header empty continuat
305
306 # Basic base64(alice:secret-passwd)
307 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
302 - Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
308 + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
309 EOF
310
311 CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
312
313 # Note that leading and trailing whitespace is important to correctly
314 # simulate a continuation/folded header.
309 - printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
310 - printf " \r\n" >>"$CHALLENGE" &&
311 - printf " param2=\"value2\"\r\n" >>"$CHALLENGE" &&
312 - printf "WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
313 - printf " p=1\r\n" >>"$CHALLENGE" &&
314 - printf " \r\n" >>"$CHALLENGE" &&
315 - printf " q=0\r\n" >>"$CHALLENGE" &&
316 - printf "WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
315 + printf "id=1 status=200\n" >"$CHALLENGE" &&
316 + printf "id=default response=WWW-Authenticate: FooBar param1=\"value1\"\r\n" >>"$CHALLENGE" &&
317 + printf "id=default response= \r\n" >>"$CHALLENGE" &&
318 + printf "id=default response= param2=\"value2\"\r\n" >>"$CHALLENGE" &&
319 + printf "id=default response=WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
320 + printf "id=default response= p=1\r\n" >>"$CHALLENGE" &&
321 + printf "id=default response= \r\n" >>"$CHALLENGE" &&
322 + printf "id=default response= q=0\r\n" >>"$CHALLENGE" &&
323 + printf "id=default response=WWW-Authenticate: Basic realm=\"example.com\"\r\n" >>"$CHALLENGE" &&
324
325 test_config_global credential.helper test-helper &&
326 git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
@@ -346,17 +353,18 @@ test_expect_success 'access using basic auth with wwwauth header mixed line-endi
353
354 # Basic base64(alice:secret-passwd)
355 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
349 - Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
356 + id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
357 EOF
358
359 CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
360
361 # Note that leading and trailing whitespace is important to correctly
362 # simulate a continuation/folded header.
356 - printf "WWW-Authenticate: FooBar param1=\"value1\"\r\n" >"$CHALLENGE" &&
357 - printf " \r\n" >>"$CHALLENGE" &&
358 - printf "\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
359 - printf "WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
363 + printf "id=1 status=200\n" >"$CHALLENGE" &&
364 + printf "id=default response=WWW-Authenticate: FooBar param1=\"value1\"\r\n" >>"$CHALLENGE" &&
365 + printf "id=default response= \r\n" >>"$CHALLENGE" &&
366 + printf "id=default response=\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
367 + printf "id=default response=WWW-Authenticate: Basic realm=\"example.com\"" >>"$CHALLENGE" &&
368
369 test_config_global credential.helper test-helper &&
370 git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&
@@ -389,15 +397,16 @@ test_expect_success 'access using bearer auth' '
397
398 # Basic base64(a-git-token)
399 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
392 - Bearer YS1naXQtdG9rZW4=
400 + id=1 creds=Bearer YS1naXQtdG9rZW4=
401 EOF
402
403 CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
404
405 cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
398 - WWW-Authenticate: FooBar param1="value1" param2="value2"
399 - WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
400 - WWW-Authenticate: Basic realm="example.com"
406 + id=1 status=200
407 + id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
408 + id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
409 + id=default response=WWW-Authenticate: Basic realm="example.com"
410 EOF
411
412 test_config_global credential.helper test-helper &&
@@ -433,15 +442,16 @@ test_expect_success 'access using bearer auth with invalid credentials' '
442
443 # Basic base64(a-git-token)
444 cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
436 - Bearer YS1naXQtdG9rZW4=
445 + id=1 creds=Bearer YS1naXQtdG9rZW4=
446 EOF
447
448 CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&
449
450 cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
442 - WWW-Authenticate: FooBar param1="value1" param2="value2"
443 - WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
444 - WWW-Authenticate: Basic realm="example.com"
451 + id=1 status=200
452 + id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
453 + id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
454 + id=default response=WWW-Authenticate: Basic realm="example.com"
455 EOF
456
457 test_config_global credential.helper test-helper &&