#!/usr/bin/env bash
#
# SIGN IN TO THE PRODUCTION IMAGE THE WAY A PERSON DOES, SECOND STEP INCLUDED
# (round 891, ADR 0032 (4)).
#
#   bin/prod-image-sign-in.sh BASE_URL WORKSPACE EMAIL PASSWORD KEY_FILE COOKIE_JAR
#
# An Owner, Admin or Compliance account is never signed in by its password
# alone. When it has no second factor yet, the code step shows a new key; this
# keeps that key in KEY_FILE and answers with a code from it, which sets the
# factor up. When it has one, the key is read back from KEY_FILE. The code is
# computed from RFC 6238 (SHA-1, 6 digits, 30 seconds), never asked of the
# application.
#
# A code is accepted once per 30-second step (§8.1), so two sign-ins inside one
# step would have the second refused as a replay. A refused code is not
# retried in the same step: this waits for the next and starts again, up to
# three times. On success the session is in COOKIE_JAR and it prints "signed
# in"; otherwise it says why and exits non-zero.
#
# Needs curl and python3.

set -uo pipefail

base=${1:?usage: prod-image-sign-in.sh BASE WORKSPACE EMAIL PASSWORD KEY_FILE COOKIE_JAR}
workspace=$2
email=$3
password=$4
key_file=$5
jar=$6
here=$(cd "$(dirname "$0")" && pwd)
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT

page() { curl -sk -c "$jar" -b "$jar" -o "$work/body" -m 30 "$@"; }
csrf() { grep -oE 'name="_csrf" value="[^"]+"' "$work/body" | head -1 | sed -E 's/.*value="([^"]+)"/\1/'; }

for _ in 1 2 3; do
    page "$base/login" -w '' >/dev/null
    where=$(page -w '%{http_code} %{redirect_url}' --data-urlencode "_csrf=$(csrf)" --data-urlencode "workspace=$workspace" \
        --data-urlencode "email=$email" --data-urlencode "password=$password" "$base/login")
    case $where in
        "302 $base/dashboard") echo "signed in"; exit 0 ;;
        "302 $base/login/mfa") ;;
        *) echo "FAIL  the password step answered $where"; exit 1 ;;
    esac

    page "$base/login/mfa" >/dev/null
    shown=$(grep -oE 'id="setup-key"[^>]*>[A-Z2-7]+<' "$work/body" | head -1 | sed -E 's/.*>([A-Z2-7]+)</\1/')
    if [ -n "$shown" ]; then
        printf '%s' "$shown" > "$key_file"
    fi
    [ -s "$key_file" ] || { echo "FAIL  a code is asked for and no key is known for $email"; exit 1; }

    code=$(python3 "$here/totp-code.py" "$(cat "$key_file")")
    where=$(page -w '%{http_code} %{redirect_url}' --data-urlencode "_csrf=$(csrf)" --data-urlencode "code=$code" "$base/login/mfa")
    if [ "$where" = "302 $base/dashboard" ]; then
        echo "signed in"
        exit 0
    fi

    # Refused: most likely this step's code was already spent by the sign-in
    # before. Wait for the next step and start again from the password.
    sleep $((31 - $(date +%s) % 30))
done

echo "FAIL  the code step refused three codes in three steps for $email"
exit 1
