#!/bin/sh

test_description='test http auth header and credential helper interop'

. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh

enable_cgipassauth
if ! test_have_prereq CGIPASSAUTH
then
	skip_all="no CGIPassAuth support"
	test_done
fi
start_httpd

test_expect_success 'setup_credential_helper' '
	mkdir "$TRASH_DIRECTORY/bin" &&
	PATH=$PATH:"$TRASH_DIRECTORY/bin" &&
	export PATH &&

	CREDENTIAL_HELPER="$TRASH_DIRECTORY/bin/git-credential-test-helper" &&
	write_script "$CREDENTIAL_HELPER" <<-\EOF
	cmd=$1
	teefile=$cmd-query-temp.cred
	catfile=$cmd-reply.cred
	sed -n -e "/^$/q" -e "p" >>$teefile
	state=$(sed -ne "s/^state\[\]=helper://p" "$teefile")
	if test -z "$state"
	then
		mv "$teefile" "$cmd-query.cred"
	else
		mv "$teefile" "$cmd-query-$state.cred"
		catfile="$cmd-reply-$state.cred"
	fi
	if test "$cmd" = "get"
	then
		cat $catfile
	fi
	EOF
'

set_credential_reply () {
	local suffix="$(test -n "$2" && echo "-$2")"
	cat >"$TRASH_DIRECTORY/$1-reply$suffix.cred"
}

expect_credential_query () {
	local suffix="$(test -n "$2" && echo "-$2")"
	cat >"$TRASH_DIRECTORY/$1-expect$suffix.cred" &&
	test_cmp "$TRASH_DIRECTORY/$1-expect$suffix.cred" \
		 "$TRASH_DIRECTORY/$1-query$suffix.cred"
}

per_test_cleanup () {
	rm -f *.cred &&
	rm -f "$HTTPD_ROOT_PATH"/custom-auth.valid \
	      "$HTTPD_ROOT_PATH"/custom-auth.challenge
}

test_expect_success 'setup repository' '
	test_commit foo &&
	git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
	git push --mirror "$HTTPD_DOCUMENT_ROOT_PATH/repo.git"
'

test_expect_success 'access using basic auth' '
	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 &&
	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	EOF

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default response=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 &&
	capability[]=authtype
	capability[]=state
	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 via authtype' '
	test_when_finished "per_test_cleanup" &&

	set_credential_reply get <<-EOF &&
	capability[]=authtype
	authtype=Basic
	credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	EOF

	# Basic base64(alice:secret-passwd)
	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	EOF

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default response=WWW-Authenticate: Basic realm="example.com"
	EOF

	test_config_global credential.helper test-helper &&
	GIT_CURL_VERBOSE=1 git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&

	expect_credential_query get <<-EOF &&
	capability[]=authtype
	capability[]=state
	protocol=http
	host=$HTTPD_DEST
	wwwauth[]=Basic realm="example.com"
	EOF

	expect_credential_query store <<-EOF
	capability[]=authtype
	authtype=Basic
	credential=YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	protocol=http
	host=$HTTPD_DEST
	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 &&
	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	EOF

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default response=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 &&
	capability[]=authtype
	capability[]=state
	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 proactive auth' '
	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 &&
	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	EOF

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default status=403
	EOF

	test_config_global credential.helper test-helper &&
	test_config_global http.proactiveAuth basic &&
	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&

	expect_credential_query get <<-EOF &&
	capability[]=authtype
	capability[]=state
	protocol=http
	host=$HTTPD_DEST
	wwwauth[]=Basic
	EOF

	expect_credential_query store <<-EOF
	protocol=http
	host=$HTTPD_DEST
	username=alice
	password=secret-passwd
	EOF
'

test_expect_success 'access using auto proactive auth with basic default' '
	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 &&
	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	EOF

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default status=403
	EOF

	test_config_global credential.helper test-helper &&
	test_config_global http.proactiveAuth auto &&
	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&

	expect_credential_query get <<-EOF &&
	capability[]=authtype
	capability[]=state
	protocol=http
	host=$HTTPD_DEST
	EOF

	expect_credential_query store <<-EOF
	protocol=http
	host=$HTTPD_DEST
	username=alice
	password=secret-passwd
	EOF
'

test_expect_success 'access using auto proactive auth with authtype from credential helper' '
	test_when_finished "per_test_cleanup" &&

	set_credential_reply get <<-EOF &&
	capability[]=authtype
	authtype=Bearer
	credential=YS1naXQtdG9rZW4=
	EOF

	# Basic base64(a-git-token)
	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
	id=1 creds=Bearer YS1naXQtdG9rZW4=
	EOF

	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default status=403
	EOF

	test_config_global credential.helper test-helper &&
	test_config_global http.proactiveAuth auto &&
	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&

	expect_credential_query get <<-EOF &&
	capability[]=authtype
	capability[]=state
	protocol=http
	host=$HTTPD_DEST
	EOF

	expect_credential_query store <<-EOF
	capability[]=authtype
	authtype=Bearer
	credential=YS1naXQtdG9rZW4=
	protocol=http
	host=$HTTPD_DEST
	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 &&
	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	EOF

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
	id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
	id=default response=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 &&
	capability[]=authtype
	capability[]=state
	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 &&
	id=1 creds=Basic YWxpY2U6c2VjcmV0LXBhc3N3ZA==
	EOF

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default response=www-authenticate: foobar param1="value1" param2="value2"
	id=default response=WWW-AUTHENTICATE: BEARER authorize_uri="id.example.com" p=1 q=0
	id=default response=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 &&
	capability[]=authtype
	capability[]=state
	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 &&
	id=1 creds=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 &&
	id=1 status=200
	id=default response=WWW-Authenticate: FooBar param1="value1"
	id=default response= param2="value2"
	id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com"
	id=default response= p=1
	id=default response= q=0
	id=default response=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 &&
	capability[]=authtype
	capability[]=state
	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 &&
	id=1 creds=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 "id=1 status=200\n" >"$CHALLENGE" &&
	printf "id=default response=WWW-Authenticate: FooBar param1=\"value1\"\r\n" >>"$CHALLENGE" &&
	printf "id=default response= \r\n" >>"$CHALLENGE" &&
	printf "id=default response= param2=\"value2\"\r\n" >>"$CHALLENGE" &&
	printf "id=default response=WWW-Authenticate: Bearer authorize_uri=\"id.example.com\"\r\n" >>"$CHALLENGE" &&
	printf "id=default response= p=1\r\n" >>"$CHALLENGE" &&
	printf "id=default response= \r\n" >>"$CHALLENGE" &&
	printf "id=default response= q=0\r\n" >>"$CHALLENGE" &&
	printf "id=default response=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 &&
	capability[]=authtype
	capability[]=state
	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 &&
	id=1 creds=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 "id=1 status=200\n" >"$CHALLENGE" &&
	printf "id=default response=WWW-Authenticate: FooBar param1=\"value1\"\r\n" >>"$CHALLENGE" &&
	printf "id=default response= \r\n" >>"$CHALLENGE" &&
	printf "id=default response=\tparam2=\"value2\"\r\n" >>"$CHALLENGE" &&
	printf "id=default response=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 &&
	capability[]=authtype
	capability[]=state
	protocol=http
	host=$HTTPD_DEST
	wwwauth[]=FooBar param1="value1" param2="value2"
	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 bearer auth' '
	test_when_finished "per_test_cleanup" &&

	set_credential_reply get <<-EOF &&
	capability[]=authtype
	authtype=Bearer
	credential=YS1naXQtdG9rZW4=
	EOF

	# Basic base64(a-git-token)
	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
	id=1 creds=Bearer YS1naXQtdG9rZW4=
	EOF

	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
	id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
	id=default response=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 &&
	capability[]=authtype
	capability[]=state
	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
	capability[]=authtype
	authtype=Bearer
	credential=YS1naXQtdG9rZW4=
	protocol=http
	host=$HTTPD_DEST
	EOF
'

test_expect_success 'access using bearer auth with invalid credentials' '
	test_when_finished "per_test_cleanup" &&

	set_credential_reply get <<-EOF &&
	capability[]=authtype
	authtype=Bearer
	credential=incorrect-token
	EOF

	# Basic base64(a-git-token)
	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
	id=1 creds=Bearer YS1naXQtdG9rZW4=
	EOF

	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=200
	id=default response=WWW-Authenticate: FooBar param1="value1" param2="value2"
	id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
	id=default response=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 &&
	capability[]=authtype
	capability[]=state
	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 erase <<-EOF
	capability[]=authtype
	authtype=Bearer
	credential=incorrect-token
	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
'

test_expect_success 'access using three-legged auth' '
	test_when_finished "per_test_cleanup" &&

	set_credential_reply get <<-EOF &&
	capability[]=authtype
	capability[]=state
	authtype=Multistage
	credential=YS1naXQtdG9rZW4=
	state[]=helper:foobar
	continue=1
	EOF

	set_credential_reply get foobar <<-EOF &&
	capability[]=authtype
	capability[]=state
	authtype=Multistage
	credential=YW5vdGhlci10b2tlbg==
	state[]=helper:bazquux
	EOF

	cat >"$HTTPD_ROOT_PATH/custom-auth.valid" <<-EOF &&
	id=1 creds=Multistage YS1naXQtdG9rZW4=
	id=2 creds=Multistage YW5vdGhlci10b2tlbg==
	EOF

	CHALLENGE="$HTTPD_ROOT_PATH/custom-auth.challenge" &&

	cat >"$HTTPD_ROOT_PATH/custom-auth.challenge" <<-EOF &&
	id=1 status=401 response=WWW-Authenticate: Multistage challenge="456"
	id=1 status=401 response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
	id=2 status=200
	id=default response=WWW-Authenticate: Multistage challenge="123"
	id=default response=WWW-Authenticate: Bearer authorize_uri="id.example.com" p=1 q=0
	EOF

	test_config_global credential.helper test-helper &&
	git ls-remote "$HTTPD_URL/custom_auth/repo.git" &&

	expect_credential_query get <<-EOF &&
	capability[]=authtype
	capability[]=state
	protocol=http
	host=$HTTPD_DEST
	wwwauth[]=Multistage challenge="123"
	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
	EOF

	expect_credential_query get foobar <<-EOF &&
	capability[]=authtype
	capability[]=state
	authtype=Multistage
	protocol=http
	host=$HTTPD_DEST
	wwwauth[]=Multistage challenge="456"
	wwwauth[]=Bearer authorize_uri="id.example.com" p=1 q=0
	state[]=helper:foobar
	EOF

	expect_credential_query store bazquux <<-EOF
	capability[]=authtype
	capability[]=state
	authtype=Multistage
	credential=YW5vdGhlci10b2tlbg==
	protocol=http
	host=$HTTPD_DEST
	state[]=helper:bazquux
	EOF
'

test_lazy_prereq NTLM 'curl --version | grep -q NTLM'

test_expect_success NTLM 'access using NTLM auth' '
	test_when_finished "per_test_cleanup" &&

	set_credential_reply get <<-EOF &&
	username=user
	password=pwd
	EOF

	test_config_global credential.helper test-helper &&
	test_must_fail env GIT_TRACE_CURL=1 git \
		ls-remote "$HTTPD_URL/ntlm_auth/repo.git" 2>err &&
	test_grep "allowNTLMAuth" err &&

	# Can be enabled via config
	GIT_TRACE_CURL=1 git -c http.$HTTPD_URL.allowNTLMAuth=true \
		ls-remote "$HTTPD_URL/ntlm_auth/repo.git" &&

	# Or via credential helper responding with ntlm=allow
	set_credential_reply get <<-EOF &&
	username=user
	password=pwd
	ntlm=allow
	EOF

	git ls-remote "$HTTPD_URL/ntlm_auth/repo.git"
'

test_done
