merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

setup.sh (8387B)


      1 #!/bin/sh
      2 # This file is in the public domain
      3 
      4 # Script to be inlined into the main test scripts. Defines function 'setup()'
      5 # which wraps around 'taler-unified-setup.sh' to launch GNU Taler services.
      6 # Call setup() with the arguments to pass to 'taler-unified-setup'. setup()
      7 # will then launch GNU Taler, wait for the process to be complete before
      8 # returning. The script will also install an exit handler to ensure the GNU
      9 # Taler processes are stopped when the shell exits.
     10 
     11 set -eu
     12 
     13 unset XDG_DATA_HOME
     14 unset XDG_CONFIG_HOME
     15 
     16 
     17 # Cleanup to run whenever we exit
     18 function exit_cleanup()
     19 {
     20     if [ ! -z ${SETUP_PID+x} ]
     21     then
     22         echo "Killing taler-unified-setup ($SETUP_PID)" >&2
     23         kill -TERM "$SETUP_PID" 2> /dev/null || true
     24         wait "$SETUP_PID" 2> /dev/null || true
     25     fi
     26 }
     27 
     28 # Install cleanup handler (except for kill -9)
     29 trap exit_cleanup EXIT
     30 
     31 function setup()
     32 {
     33     echo "Starting test system ..." >&2
     34     # Create a named pipe in a temp directory we own.
     35     FIFO_DIR=$(mktemp -p "${TMPDIR:-/tmp}" -d fifo-XXXXXX)
     36     FIFO_OUT=$(echo "$FIFO_DIR/out")
     37     mkfifo "$FIFO_OUT"
     38     # Open pipe as FD 3 (RW) and FD 4 (RO)
     39     exec 3<> "$FIFO_OUT" 4< "$FIFO_OUT"
     40     rm -rf "$FIFO_DIR"
     41     # We require '-W' for our termination logic to work.
     42     taler-unified-setup.sh -W "$@" >&3 &
     43     SETUP_PID=$!
     44     # Close FD3
     45     exec 3>&-
     46     # Wait for the 'READY:' line. Reading until EOF instead of using
     47     # 'sed -u /READY:/ q' lets us tell "the system came up" apart from
     48     # "taler-unified-setup.sh died first": on EOF without READY the test
     49     # would otherwise carry on against services that are not running and
     50     # fail much later with a confusing error.
     51     READY=0
     52     while IFS= read -r LINE <&4
     53     do
     54         printf '%s\n' "$LINE"
     55         case "$LINE" in
     56             READY:*)
     57                 READY=1
     58                 break
     59                 ;;
     60         esac
     61     done
     62     # Close FD4
     63     exec 4>&-
     64     if [ "$READY" != "1" ]
     65     then
     66         SETUP_STATUS=0
     67         wait "$SETUP_PID" || SETUP_STATUS=$?
     68         unset SETUP_PID
     69         if [ "$SETUP_STATUS" = "77" ]
     70         then
     71             exit_skip "taler-unified-setup.sh could not launch the test system"
     72         fi
     73         exit_fail "taler-unified-setup.sh died with status $SETUP_STATUS before the test system was ready"
     74     fi
     75     echo "Test system ready" >&2
     76 }
     77 
     78 # Exit, with status code "skip" (no 'real' failure)
     79 function exit_fail() {
     80     echo "$@" >&2
     81     exit 1
     82 }
     83 
     84 # Exit, with status code "skip" (no 'real' failure)
     85 function exit_skip() {
     86     echo "SKIPPING: $1"
     87     exit 77
     88 }
     89 
     90 function get_payto_uri() {
     91     libeufin-bank create-account -u "$1" -p "$2" --name "$1" 2> /dev/null
     92 }
     93 
     94 # Print the base URL of merchant instance $1 of the backend under test.
     95 function merchant_instance_url()
     96 {
     97     if [ "$1" = "admin" ]
     98     then
     99         echo "http://localhost:9966/"
    100     else
    101         echo "http://localhost:9966/instances/$1/"
    102     fi
    103 }
    104 
    105 # Log in to merchant instance $1 with password $2 (Basic authentication
    106 # at POST /private/token) and print the access token.  $3 is the scope
    107 # of the token, "all" by default.  Instances that require MFA for the
    108 # login need merchant_login_mfa() instead.
    109 function merchant_login()
    110 {
    111     local RESPONSE
    112     local STATUS
    113 
    114     RESPONSE=$(mktemp -p "${TMPDIR:-/tmp}" merchant-login-XXXXXX)
    115     STATUS=$(curl -s -X POST \
    116         -H "Content-Type: application/json" \
    117         -H "Authorization: Basic $(printf '%s:%s' "$1" "$2" | base64 -w0)" \
    118         "$(merchant_instance_url "$1")private/token" \
    119         -d '{"scope":"'"${3:-all}"'","duration":{"d_us":86400000000}}' \
    120         -w "%{http_code}" \
    121         -o "$RESPONSE")
    122     if [ "$STATUS" != "200" ]
    123     then
    124         jq < "$RESPONSE" >&2 || true
    125         rm -f "$RESPONSE"
    126         exit_fail "Expected 200 OK for the login to instance $1. Got: $STATUS"
    127     fi
    128     jq -er .access_token < "$RESPONSE"
    129     rm -f "$RESPONSE"
    130 }
    131 
    132 # Directory where test_email_helper.sh and test_sms_helper.sh store the
    133 # last TAN and the address it was sent to.  Tests set
    134 # TALER_MERCHANT_TEST_TAN_DIR before setup() so that the merchant passes
    135 # it on to the helpers.
    136 function tan_dir()
    137 {
    138     echo "${TALER_MERCHANT_TEST_TAN_DIR:-/tmp}"
    139 }
    140 
    141 # Have the merchant transmit challenge $1 and solve it with the TAN the
    142 # helper received.  $2 is the channel ("email" or "sms"), $3 the address
    143 # the TAN must have been sent to.
    144 function solve_challenge()
    145 {
    146     local RESPONSE
    147     local STATUS
    148     local TAN
    149     local ADDRESS
    150 
    151     RESPONSE=$(mktemp -p "${TMPDIR:-/tmp}" merchant-challenge-XXXXXX)
    152     rm -f "$(tan_dir)/test-merchant-$2-tan.txt" \
    153        "$(tan_dir)/test-merchant-$2-address.txt"
    154     STATUS=$(curl -s -X POST \
    155         -H "Content-Type: application/json" \
    156         "http://localhost:9966/challenge/$1" \
    157         -d '{}' \
    158         -w "%{http_code}" \
    159         -o "$RESPONSE")
    160     if [ "$STATUS" != "200" ]
    161     then
    162         jq < "$RESPONSE" >&2 || true
    163         exit_fail "Expected challenge transmission to return 200 OK. Got: $STATUS"
    164     fi
    165     TAN=$(head -n1 "$(tan_dir)/test-merchant-$2-tan.txt" | awk '{print $1}')
    166     ADDRESS=$(cat "$(tan_dir)/test-merchant-$2-address.txt")
    167     if [ "$ADDRESS" != "$3" ]
    168     then
    169         exit_fail "Expected $2 address '$3'. Got: $ADDRESS"
    170     fi
    171     STATUS=$(curl -s -X POST \
    172         -H "Content-Type: application/json" \
    173         "http://localhost:9966/challenge/$1/confirm" \
    174         -d '{"tan":"'"$TAN"'"}' \
    175         -w "%{http_code}" \
    176         -o "$RESPONSE")
    177     if [ "$STATUS" != "204" ]
    178     then
    179         jq < "$RESPONSE" >&2 || true
    180         exit_fail "Expected challenge confirmation to return 204 No Content. Got: $STATUS"
    181     fi
    182     rm -f "$RESPONSE"
    183 }
    184 
    185 # Solve the challenges listed in the 202 response in file $1: all of them
    186 # if the response says "combi_and", otherwise only the first.  $2 and $3
    187 # are the e-mail address and the phone number the TANs must go to.
    188 # Prints the comma-separated IDs of the solved challenges.
    189 function solve_response_challenges()
    190 {
    191     local IDS=""
    192     local CHALLENGE_ID
    193     local CHANNEL
    194     local ADDRESS
    195     local FILTER='.challenges[]'
    196 
    197     if [ "$(jq -r .combi_and < "$1")" != "true" ]
    198     then
    199         FILTER='.challenges[0]'
    200     fi
    201     while IFS=$'\t' read -r CHALLENGE_ID CHANNEL
    202     do
    203         case "$CHANNEL" in
    204         email)
    205             ADDRESS="$2"
    206             ;;
    207         sms)
    208             ADDRESS="$3"
    209             ;;
    210         *)
    211             exit_fail "Unexpected TAN channel: $CHANNEL"
    212             ;;
    213         esac
    214         solve_challenge "$CHALLENGE_ID" "$CHANNEL" "$ADDRESS"
    215         IDS="${IDS:+$IDS,}$CHALLENGE_ID"
    216     done < <(jq -r "$FILTER"' | [.challenge_id, .tan_channel] | @tsv' "$1")
    217     echo "$IDS"
    218 }
    219 
    220 # Log in to MFA-protected merchant instance $1 with password $2, solving
    221 # the challenges the login asks for, and print the access token.  $3 and
    222 # $4 are the e-mail address and the phone number of the instance.
    223 function merchant_login_mfa()
    224 {
    225     local RESPONSE
    226     local STATUS
    227     local BASIC
    228     local BODY='{"scope":"all","duration":{"d_us":86400000000}}'
    229     local IDS
    230 
    231     RESPONSE=$(mktemp -p "${TMPDIR:-/tmp}" merchant-login-XXXXXX)
    232     BASIC="Authorization: Basic $(printf '%s:%s' "$1" "$2" | base64 -w0)"
    233     STATUS=$(curl -s -X POST \
    234         -H "Content-Type: application/json" \
    235         -H "$BASIC" \
    236         "$(merchant_instance_url "$1")private/token" \
    237         -d "$BODY" \
    238         -w "%{http_code}" \
    239         -o "$RESPONSE")
    240     if [ "$STATUS" != "202" ]
    241     then
    242         jq < "$RESPONSE" >&2 || true
    243         exit_fail "Expected 202 Accepted for the login to instance $1. Got: $STATUS"
    244     fi
    245     IDS=$(solve_response_challenges "$RESPONSE" "$3" "$4")
    246     STATUS=$(curl -s -X POST \
    247         -H "Content-Type: application/json" \
    248         -H "$BASIC" \
    249         -H "Taler-Challenge-Ids: $IDS" \
    250         "$(merchant_instance_url "$1")private/token" \
    251         -d "$BODY" \
    252         -w "%{http_code}" \
    253         -o "$RESPONSE")
    254     if [ "$STATUS" != "200" ]
    255     then
    256         jq < "$RESPONSE" >&2 || true
    257         rm -f "$RESPONSE"
    258         exit_fail "Expected 200 OK for the login to instance $1 with solved challenges. Got: $STATUS"
    259     fi
    260     jq -er .access_token < "$RESPONSE"
    261     rm -f "$RESPONSE"
    262 }
    263 
    264 echo -n "Checking for curl ..."
    265 curl --version 2> /dev/null > /dev/null || exit_skip " no curl"
    266 echo " OK"
    267 echo -n "Checking for jq ..."
    268 jq --version 2> /dev/null > /dev/null || exit_skip " no jq"
    269 echo " OK"