#!/bin/bash
# This file is in the public domain.

set -eu

# Exit, with status code "skip" (no 'real' failure)
function exit_skip() {
    echo " SKIP: $1"
    exit 77
}

# Exit, with error message (hard failure)
function exit_fail() {
    echo " FAIL: $@"
    exit 1
}

# Cleanup to run whenever we exit
function cleanup()
{
    for n in $(jobs -p)
    do
        kill $n 2> /dev/null || true
    done
    rm -f "$LAST_RESPONSE" "$FILENAME"
    wait
}

LAST_RESPONSE=$(mktemp responseXXXXXX.log)
FILENAME="test-challenger-pkce.txt"

# Install cleanup handler (except for kill -9)
trap cleanup EXIT

export PATH="$PATH:."

echo -n "Testing for jq"
jq -h > /dev/null || exit_skip "jq required"
echo " FOUND"
echo -n "Testing for curl"
curl -h > /dev/null || exit_skip "curl required"
echo " FOUND"
echo -n "Testing for wget"
wget -h > /dev/null || exit_skip "wget required"
echo " FOUND"
echo -n "Testing for challenger-httpd ..."
challenger-httpd -h > /dev/null || exit_skip "challenger-httpd required"
echo " FOUND"

CONF="test-challenger-pkce.conf"
BURL="http://localhost:9967"
REDIRECT_URI="http://client.example.com/"

echo -n "Initialize challenger database ..."
challenger-dbinit -r -c "${CONF}" &> dbinit.log
echo " OK"

echo -n "Add challenger client ..."
CLIENT_SECRET="secret-token:secret"
challenger-admin -c "${CONF}" -a "${CLIENT_SECRET}" "${REDIRECT_URI}" &> admin.log
echo " OK"
# We run this test dbinit, thus the client ID must be 1 here:
CLIENT_ID=1

echo -n "Start challenger-httpd ..."
challenger-httpd -L INFO -c "${CONF}" &> httpd.log &

# Wait for challenger to be available
for n in $(seq 1 50)
do
    echo -n "."
    sleep 0.2
    OK=0
    # bank
    wget --tries=1 --timeout=1 "${BURL}/config" -o /dev/null -O /dev/null >/dev/null || continue
    OK=1
    break
done
if [ 1 != $OK ]
then
    exit_skip "Failed to launch challenger service"
fi


echo -n "Setup new validation process..."
STATUS=$(curl "${BURL}/setup/${CLIENT_ID}" \
    -H "Authorization: Bearer ${CLIENT_SECRET}" \
    -d '' \
    -w "%{http_code}" -s -o $LAST_RESPONSE)

if [ "$STATUS" != "200" ]
then
    exit_fail "Expected 200 OK. Got: $STATUS" $(cat $LAST_RESPONSE)
fi
NONCE=$(jq -r .nonce < "$LAST_RESPONSE")
echo " OK"

CLIENT_STATE="the-client-state"
CLIENT_SCOPE="the-client-scope"
CODE_CHALLENGE_METHOD="S256"
CODE_CHALLENGE="3FtH5SoMllpyFP-nlcdmkGYraTpnhA-9U1N6tHoUYv8"
CODE_VERIFIER="z167JIUt0F.II.qLPlCaXmL8BI6x9E-qqHAE_xEO_8p"
CODE_VERIFIER_FALSE="false-Ut0F.II.qLPlCaXmL8BI6x9E-qqHAE_xEO_8p"

echo -n "Initiating user login..."
STATUS=$(curl "${BURL}/authorize/${NONCE}" \
    -G \
    -H "Accept: application/json" \
    --data-urlencode "response_type=code" \
    --data-urlencode "client_id=${CLIENT_ID}" \
    --data-urlencode "redirect_uri=${REDIRECT_URI}" \
    --data-urlencode "state=${CLIENT_STATE}" \
    --data-urlencode "scope=${CLIENT_SCOPE}" \
    --data-urlencode "code_challenge_method=${CODE_CHALLENGE_METHOD}" \
    --data-urlencode "code_challenge=${CODE_CHALLENGE}" \
    -w "%{http_code}" -s -o $LAST_RESPONSE)

if [ "$STATUS" != "200" ]
then
    exit_fail "Expected 200 OK. Got: $STATUS" $(cat $LAST_RESPONSE)
    exit 1
fi
echo "OK"


echo -n "Initiating address submission..."
STATUS=$(curl "${BURL}/challenge/${NONCE}" \
    -X POST \
    -H "Accept: application/json" \
    --data-urlencode "filename=${FILENAME}" \
    -w "%{http_code}" -s -o $LAST_RESPONSE)

if [ "$STATUS" != "200" ]
then
    exit_fail "Expected 200 OK. Got: $STATUS" $(cat $LAST_RESPONSE)
fi
echo "OK"

PIN=$(cat ${FILENAME} | awk '{print $5}')

echo -n "Initiating PIN ${PIN} submission..."
RESULT=$(curl "${BURL}/solve/${NONCE}" \
    -X POST \
    -H "Accept: text/html" \
    --data-urlencode "pin=${PIN}" \
    -w "%{http_code} %{redirect_url}" -s -o $LAST_RESPONSE)
STATUS=$(echo "$RESULT" | awk '{print $1}')
TARGET=$(echo "$RESULT" | awk '{print $2}')

if [ "$STATUS" != "302" ]
then
    exit_fail "Expected 302. Got: $STATUS" $(cat $LAST_RESPONSE)
fi

TURL=$(echo "$TARGET" | sed -e "s/?.*//g")
TCODE=$(echo "$TARGET" | sed -e "s/.*?code=//g" -e "s/&.*//g")
TSTATE=$(echo "$TARGET" | sed -e "s/.*&state=//g")

if [ "${TURL}" != "${REDIRECT_URI}" ]
then
    exit_fail "Invalid redirect URI ${TURL} returned, wanted ${REDIRECT_URI}"
fi
if [ "${TSTATE}" != "${CLIENT_STATE}" ]
then
    exit_fail "Invalid client state ${TSTATE} returned, wanted ${CLIENT_STATE}"
fi
echo "OK"

echo -n "Requesting authorization for client with wrong code_verifier ..."
STATUS=$(curl "${BURL}/token" \
    -X POST \
    --data-urlencode "client_id=${CLIENT_ID}" \
    --data-urlencode "redirect_uri=${REDIRECT_URI}" \
    --data-urlencode "client_secret=${CLIENT_SECRET}" \
    --data-urlencode "code=${TCODE}" \
    --data-urlencode "grant_type=authorization_code" \
    --data-urlencode "code_verifier=${CODE_VERIFIER_FALSE}" \
    -w "%{http_code}" -s -o $LAST_RESPONSE)

if [ "$STATUS" != "401" ]
then
    exit_fail "Expected 401 Unauthorized. Got: $STATUS" $(cat $LAST_RESPONSE)
fi
echo "OK"

echo -n "Requesting authorization for client ..."
STATUS=$(curl "${BURL}/token" \
    -X POST \
    --data-urlencode "client_id=${CLIENT_ID}" \
    --data-urlencode "redirect_uri=${REDIRECT_URI}" \
    --data-urlencode "client_secret=${CLIENT_SECRET}" \
    --data-urlencode "code=${TCODE}" \
    --data-urlencode "grant_type=authorization_code" \
    --data-urlencode "code_verifier=${CODE_VERIFIER}" \
    -w "%{http_code}" -s -o $LAST_RESPONSE)

if [ "$STATUS" != "200" ]
then
    exit_fail "Expected 200 OK. Got: $STATUS" $(cat $LAST_RESPONSE)
fi
TOKEN_TYPE="$(cat $LAST_RESPONSE | jq -r .token_type)"
if [ "$TOKEN_TYPE" != "Bearer" ]
then
    exit_fail "Expected Bearer token. Got: $TOKEN_TYPE"
fi
ACCESS_TOKEN=$(cat $LAST_RESPONSE | jq -r .access_token)
EXPIRES_IN=$(cat $LAST_RESPONSE | jq -r .expires_in)
echo "OK"

echo -n "Requesting user information for client ..."
STATUS=$(curl "${BURL}/info" \
    -H "Authorization: Bearer ${ACCESS_TOKEN}" \
    -w "%{http_code}" -s -o $LAST_RESPONSE)
if [ "$STATUS" != "200" ]
then
    exit_fail "Expected 200 OK. Got: $STATUS" $(cat $LAST_RESPONSE)
fi

cat $LAST_RESPONSE | jq

TADDRESS=$(cat "$LAST_RESPONSE" | jq -r .address.filename)
TADDRESS_TYPE=$(cat "$LAST_RESPONSE" | jq -r .address_type)

if [ "$TADDRESS" != "$FILENAME" ]
then
    exit_fail "Expected $FILENAME. Got: $TADDRESS"
fi

if [ "$TADDRESS_TYPE" != "file-access" ]
then
    exit_fail "Expected file-access. Got: $TADDRESS_TYPE"
fi
echo "OK"


exit 0
