#!/bin/bash set -eu #set -x # 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: $1" exit 1 } # Cleanup to run whenever we exit function cleanup() { for n in `jobs -p` do kill $n 2> /dev/null || true done rm -rf $CONF $R1FILE $R2FILE $B1FILE $B2FILE wait } # $1=ebics username, $2=ebics partner name, $3=person name, $4=sandbox bank account name, $5=iban function prepare_sandbox_account() { echo -n "Registering $4 to the Sandbox..." export LIBEUFIN_SANDBOX_USERNAME=$4 export LIBEUFIN_SANDBOX_PASSWORD=unused libeufin-cli sandbox --sandbox-url=$SANDBOX_URL \ demobank register --name "$3" --iban $5 echo " OK" echo -n "Associating a EBICS subscriber to $4..." export LIBEUFIN_SANDBOX_USERNAME=admin libeufin-cli sandbox --sandbox-url=$SANDBOX_URL demobank new-ebicssubscriber \ --host-id $EBICS_HOST \ --user-id $1 --partner-id $2 \ --bank-account $4 # that's a username _and_ a bank account name echo " OK" unset LIBEUFIN_SANDBOX_USERNAME unset LIBEUFIN_SANDBOX_PASSWORD } function sync_providers() { infile=$1 outfile=$2 echo "Synchronizing providers" # Sync with providers (up to 3 providers aren't synced here) for x in 1 2 3; do echo "Synchronizing providers (round $x)" anastasis-reducer sync_providers < $infile > $outfile 2> /dev/null || true CODE=$(jq -r -e ".code // 0" < $outfile) # ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED # FIXME: Temporary workaround for C reducer. See #7227. if test "$CODE" = "8420"; then # restore previous non-error state cat $infile > $outfile break fi # ANASTASIS_REDUCER_ACTION_INVALID if test "$CODE" = "8400"; then # restore previous non-error state cat $infile > $outfile break fi if test "$CODE" != "0"; then exit_fail "Expected no error or 8420/8400, got $CODE" fi cat $outfile > $infile done echo "Providers synced." } # Install cleanup handler (except for kill -9) trap cleanup EXIT # Transfer only from debit to credit/anastasis account. # This function moves funds directly at the Sandbox. No need # to pass through the Nexus+Ebics layer to issue the payment # $1 = amount ($CURRENCY:X.Y), $2 = subject. function wire_transfer_to_anastasis() { echo -n "Initiating wire transfer ..." libeufin-sandbox make-transaction \ --debit-account=sandbox-account-debit \ --credit-account=sandbox-account-credit "$1" "$2" &> libeufin-transfer-initiate.out echo " OK" echo -n "Syncing nexus with sandbox ..." export LIBEUFIN_NEXUS_USERNAME=$CREDIT_USERNAME export LIBEUFIN_NEXUS_PASSWORD=$CREDIT_PASSWORD libeufin-cli accounts fetch-transactions nexus-bankaccount-credit &> libeufin-transfer-fetch.out echo " OK" } # $1 = facade base URL. Merely a debug utility. function see_anastasis_transactions_via_facade() { curl -s --user "$CREDIT_USERNAME:$CREDIT_PASSWORD" "${1}history/incoming?delta=5" | jq } # $1 = ebics user id, $2 = ebics partner, $3 = bank connection name # $4 = bank account name local to Nexus, $5 = bank account name as known # by Sandbox function prepare_nexus_account() { echo -n "Making bank connection $3 ..." libeufin-cli connections new-ebics-connection \ --ebics-url="${SANDBOX_URL}ebicsweb" \ --host-id=$EBICS_HOST \ --partner-id=$2 \ --ebics-user-id=$1 \ $3 > /dev/null echo " OK" echo -n "Connecting $3 ..." libeufin-cli connections connect $3 > /dev/null echo " OK" echo -n "Importing Sandbox bank account ($5) to Nexus ($4) ..." libeufin-cli connections download-bank-accounts $3 > /dev/null libeufin-cli connections import-bank-account \ --offered-account-id=$5 --nexus-bank-account-id=$4 $3 > /dev/null echo " OK" } # Configuration file will be edited, so we create one # from the template. CONF=`mktemp test_free_reducerXXXXXX.conf` cp test_free_reducer.conf $CONF B1FILE=`mktemp test_reducer_stateB1XXXXXX` B2FILE=`mktemp test_reducer_stateB2XXXXXX` R1FILE=`mktemp test_reducer_stateR1XXXXXX` R2FILE=`mktemp test_reducer_stateR2XXXXXX` export CONF export B2FILE export B1FILE export R2FILE export R1FILE echo -n "Testing for libeufin-cli" libeufin-cli --version > /dev/null || exit_skip "libeufin-cli required" echo " FOUND" echo -n "Testing for libeufin-nexus" libeufin-nexus --version > /dev/null || exit_skip "libeufin-nexus required" echo " FOUND" echo -n "Testing for libeufin-sandbox" libeufin-sandbox --version > /dev/null || exit_skip "libeufin-sandbox required" echo " FOUND" # Check we can actually run echo -n "Testing for jq" jq -h > /dev/null || exit_skip "jq required" echo " FOUND" echo -n "Testing for anastasis-reducer ..." anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required" echo " FOUND" export LIBEUFIN_NEXUS_DB_CONNECTION="jdbc:sqlite:$(mktemp -u /tmp/nexus-db-XXXXXX.sqlite)" export LIBEUFIN_SANDBOX_DB_CONNECTION="jdbc:sqlite:$(mktemp -u /tmp/sandbox-db-XXXXXX.sqlite)" NEXUS_URL="http://localhost:5001/" SANDBOX_URL="http://localhost:5000/" echo -n "Starting Nexus ..." libeufin-nexus serve &> nexus.log & nexus_pid=$! if ! curl -s --retry 5 --retry-connrefused $NEXUS_URL > /dev/null; then exit_skip "Could not launch Nexus" fi echo " OK" echo -n "Configuring Sandbox..." libeufin-sandbox config default &> sandbox.log echo " OK" echo -n "Starting Sandbox ..." libeufin-sandbox serve --no-auth &> sandbox.log & sandbox_pid=$! if ! curl -s --retry 5 --retry-connrefused $SANDBOX_URL > /dev/null; then exit_skip "Could not launch Sandbox" fi echo " OK" CURRENCY="EUR" # CURRENCY="TESTKUDOS" EBICS_HOST="ebicstesthost" export IBAN_CREDIT="DE89370400440532013000" export IBAN_DEBIT="FR1420041010050500013M02606" echo -n "Preparing Sandbox (creating the EBICS host) ..." libeufin-cli \ sandbox --sandbox-url=$SANDBOX_URL \ ebicshost create \ --host-id=$EBICS_HOST echo " OK" PERSON_CREDIT_NAME="Person Credit" echo -n "Preparing accounts ..." # note: Ebisc schema doesn't allow dashed names. prepare_sandbox_account \ ebicsuserCredit \ ebicspartnerCredit \ "${PERSON_CREDIT_NAME}" \ sandbox-account-credit \ $IBAN_CREDIT prepare_sandbox_account \ ebicsuserDebit \ ebicspartnerDebit \ "Person Debit" \ sandbox-account-debit \ $IBAN_DEBIT echo "Sandbox preparation done" echo -n "Preparing Nexus ..." export LIBEUFIN_NEXUS_URL=$NEXUS_URL # Make debit user, will buy Anastasis services. DEBIT_USERNAME=anastasis-debit-user DEBIT_PASSWORD=anastasis-debit-password libeufin-nexus superuser $DEBIT_USERNAME --password=$DEBIT_PASSWORD echo " OK" export LIBEUFIN_NEXUS_USERNAME=$DEBIT_USERNAME export LIBEUFIN_NEXUS_PASSWORD=$DEBIT_PASSWORD # Make credit user, will be Anastasis client. CREDIT_USERNAME=anastasis-credit-user CREDIT_PASSWORD=anastasis-credit-password echo -n "Create credit user (for anastasis) at Nexus ..." libeufin-nexus superuser $CREDIT_USERNAME --password=$CREDIT_PASSWORD echo " OK" export LIBEUFIN_NEXUS_USERNAME=$CREDIT_USERNAME export LIBEUFIN_NEXUS_PASSWORD=$CREDIT_PASSWORD prepare_nexus_account \ ebicsuserCredit \ ebicspartnerCredit \ bankconnection-credit \ nexus-bankaccount-credit \ sandbox-account-credit echo -n "Create facade ..." libeufin-cli facades new-anastasis-facade \ --currency=$CURRENCY \ --facade-name=facade-credit \ bankconnection-credit nexus-bankaccount-credit echo " OK" FACADE_URL=$(libeufin-cli facades list | jq .facades[0].baseUrl | tr -d \") ## Reach facade with: $FACADE_URL + $CREDIT_USERNAME + $CREDIT_PASSWORD echo -n "Initialize Anastasis database ..." # Name of the Postgres database we will use for the script. # Will be dropped, do NOT use anything that might be used # elsewhere TARGET_DB=`anastasis-config -c $CONF -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"` dropdb $TARGET_DB >/dev/null 2>/dev/null || true createdb $TARGET_DB || exit_skip "Could not create database $TARGET_DB" anastasis-dbinit -c $CONF 2> anastasis-dbinit.log echo " OK" echo -n "Configuring Anastasis IBAN account ..." anastasis-config -c $CONF \ -s authorization-iban \ -o CREDIT_IBAN \ -V "${IBAN_CREDIT}" anastasis-config -c $CONF \ -s authorization-iban \ -o BUSINESS_NAME \ -V "${PERSON_CREDIT_NAME}" anastasis-config -c $CONF \ -s authorization-iban \ -o WIRE_GATEWAY_URL \ -V "${FACADE_URL}" anastasis-config -c $CONF \ -s authorization-iban \ -o WIRE_GATEWAY_AUTH_METHOD \ -V "basic" anastasis-config -c $CONF \ -s authorization-iban \ -o USERNAME \ -V "${LIBEUFIN_NEXUS_USERNAME}" anastasis-config -c $CONF \ -s authorization-iban \ -o PASSWORD \ -V "${LIBEUFIN_NEXUS_PASSWORD}" echo " OK" echo -n "Launching Anastasis service ..." PREFIX="" #valgrind $PREFIX anastasis-httpd -c $CONF -L INFO 2> anastasis-httpd_1.log & echo " OK" echo -n "Waiting for Anastasis service ..." # Wait for Anastasis service to be available for n in `seq 1 50` do echo -n "." sleep 0.1 OK=0 # anastasis_01 wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue OK=1 break done if [ 1 != $OK ] then exit_skip "Failed to launch Anastasis service" fi echo "OK" echo -n "Running backup logic ...," anastasis-reducer -b > $B1FILE echo -n "." anastasis-reducer -a \ '{"continent": "Demoworld"}' \ select_continent < $B1FILE > $B2FILE echo -n "." anastasis-reducer -a \ '{"country_code": "xx" }' \ select_country < $B2FILE > $B1FILE 2>> test_reducer.err echo -n "." anastasis-reducer -a \ '{"identity_attributes": { "full_name": "Max Musterman", "sq_number": "4", "birthdate": "2000-01-01"}}' \ enter_user_attributes < $B1FILE > $B2FILE 2>> test_reducer.err echo -n "," cat $B2FILE > $B1FILE sync_providers $B1FILE $B2FILE echo -n "," BASEIBAN=`echo -n $IBAN_DEBIT | gnunet-base32` anastasis-reducer -a \ "$(jq -n '{ authentication_method: { type: "iban", instructions: "Send me your money!", challenge: $CHALLENGE } }' \ --arg CHALLENGE "$BASEIBAN" )" \ add_authentication < $B2FILE > $B1FILE 2>> test_reducer.err echo -n "." # "91GPWWR" encodes "Hans" anastasis-reducer -a \ '{"authentication_method": { "type": "question", "instructions": "What is your name?", "challenge": "91GPWWR" } }' \ add_authentication < $B1FILE > $B2FILE 2>> test_reducer.err echo -n "." mv $B2FILE $B1FILE # Finished adding authentication methods anastasis-reducer \ next < $B1FILE > $B2FILE 2>> test_reducer.err echo -n "," # Finished policy review anastasis-reducer \ next < $B2FILE > $B1FILE 2>> test_reducer.err echo -n "." # Note: 'secret' must here be a Crockford base32-encoded value anastasis-reducer -a \ '{"secret": { "value" : "VERYHARDT0GVESSSECRET", "mime" : "text/plain" }}' \ enter_secret < $B1FILE > $B2FILE 2>> test_reducer.err mv $B2FILE $B1FILE anastasis-reducer next < $B1FILE > $B2FILE 2>> test_reducer.err echo " OK" echo -n "Final backup checks ..." STATE=`jq -r -e .backup_state < $B2FILE` if test "$STATE" != "BACKUP_FINISHED" then exit_fail "Expected new state to be 'BACKUP_FINISHED', got '$STATE'" fi jq -r -e .core_secret < $B2FILE > /dev/null && exit_fail "'core_secret' was not cleared upon success" echo " OK" echo -n "Running recovery basic logic ..." anastasis-reducer -r > $R1FILE anastasis-reducer -a \ '{"continent": "Demoworld"}' \ select_continent < $R1FILE > $R2FILE anastasis-reducer -a \ '{"country_code": "xx", "currencies":["TESTKUDOS"]}' \ select_country < $R2FILE > $R1FILE 2>> test_reducer.err anastasis-reducer -a '{"identity_attributes": { "full_name": "Max Musterman", "sq_number": "4", "birthdate": "2000-01-01" }}' enter_user_attributes < $R1FILE > $R2FILE 2>> test_reducer.err STATE=`jq -r -e .recovery_state < $R2FILE` if test "$STATE" != "SECRET_SELECTING" then exit_fail "Expected new state to be 'SECRET_SELECTING', got '$STATE'" fi echo " OK" echo -n "Adding provider (to ensure it is loaded)" anastasis-reducer -a '{"provider_url" : "http://localhost:8086/" }' add_provider < $R2FILE > $R1FILE echo " OK" echo -n "Selecting secret to recover" anastasis-reducer -a '{"attribute_mask": 0, "providers" : [ { "version": 1, "url" : "http://localhost:8086/" } ] }' \ select_version < $R1FILE > $R2FILE 2>> test_reducer.err STATE=`jq -r -e .recovery_state < $R2FILE` if test "$STATE" != "CHALLENGE_SELECTING" then exit_fail "Expected new state to be 'CHALLENGE_SELECTING', got '$STATE'" fi echo " OK" cat $R2FILE > $R1FILE sync_providers $R1FILE $R2FILE echo -n "Running challenge selection logic ..." UUID0=`jq -r -e .recovery_information.challenges[0].uuid < $R2FILE` UUID1=`jq -r -e .recovery_information.challenges[1].uuid < $R2FILE` UUID0Q=`jq -r -e .recovery_information.challenges[0].instructions < $R2FILE` UUID1Q=`jq -r -e .recovery_information.challenges[1].instructions < $R2FILE` if test "$UUID1Q" = 'What is your name?' then NAME_UUID=$UUID1 IBAN_UUID=$UUID0 else NAME_UUID=$UUID0 IBAN_UUID=$UUID1 fi echo "OK" echo -n "Solving first challenge ..." anastasis-reducer -a \ "$(jq -n ' { uuid: $UUID }' \ --arg UUID "$NAME_UUID" )" \ select_challenge < $R2FILE > $R1FILE 2>> test_reducer.err anastasis-reducer -a '{"answer": "Hans"}' \ solve_challenge < $R1FILE > $R2FILE echo "OK" echo -n "Solving IBAN challenge ..." anastasis-reducer -a \ "$(jq -n ' { uuid: $UUID }' \ --arg UUID "$IBAN_UUID" )" \ select_challenge < $R2FILE > $R1FILE 2>> test_reducer.err echo "OK" METHOD=`jq -r -e .challenge_feedback.\"$IBAN_UUID\".state < $R1FILE` if test "$METHOD" != "iban-instructions" then exit_fail "Expected method to be 'iban-instructions', got ${METHOD}" fi ACC=`jq -r -e .challenge_feedback.\"$IBAN_UUID\".target_iban < $R1FILE` if test "$ACC" != ${IBAN_CREDIT} then exit_fail "Expected account to be ${IBAN_CREDIT}, got ${ACC}" fi anastasis-reducer \ back < $R1FILE > $R2FILE 2>> test_reducer.err AMOUNT=`jq -r -e .challenge_feedback.\"$IBAN_UUID\".challenge_amount < $R1FILE` SUBJECT=`jq -r -e .challenge_feedback.\"$IBAN_UUID\".wire_transfer_subject < $R1FILE` echo -n "Performing authorization wire transfer ${SUBJECT} ..." wire_transfer_to_anastasis "${AMOUNT}" "${SUBJECT}" echo " OK" echo -n "Triggering inbound check ..." anastasis-helper-authorization-iban -c $CONF -t -L INFO echo " OK" # Now we should get the secret... echo -n "Polling for recovery ..." anastasis-reducer poll -L INFO < $R2FILE > $R1FILE echo " OK" echo -n "Checking recovered secret ..." # finally: check here that we recovered the secret... STATE=`jq -r -e .recovery_state < $R1FILE` if test "$STATE" != "RECOVERY_FINISHED" then jq -e . $R1FILE exit_fail "Expected new state to be 'RECOVERY_FINISHED', got '$STATE'" fi SECRET=`jq -r -e .core_secret.value < $R1FILE` if test "$SECRET" != "VERYHARDT0GVESSSECRET" then jq -e . $R1FILE exit_fail "Expected recovered secret to be 'VERYHARDT0GVESSSECRET', got '$SECRET'" fi MIME=`jq -r -e .core_secret.mime < $R1FILE` if test "$MIME" != "text/plain" then jq -e . $R1FILE exit_fail "Expected recovered mime to be 'text/plain', got '$MIME'" fi echo " OK" exit 0