anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

test_iban.sh (13767B)


      1 #!/bin/bash
      2 # This file is in the public domain.
      3 
      4 set -eu
      5 #set -x
      6 
      7 # Exit, with status code "skip" (no 'real' failure)
      8 function exit_skip() {
      9     echo " SKIP: $1"
     10     exit 77
     11 }
     12 
     13 # Exit, with error message (hard failure)
     14 function exit_fail() {
     15     echo " FAIL: $1"
     16     exit 1
     17 }
     18 
     19 # Cleanup to run whenever we exit
     20 function cleanup()
     21 {
     22     for n in $(jobs -p)
     23     do
     24         kill "$n" 2> /dev/null || true
     25     done
     26     rm -rf "$CONF" "$BANK_CONF" "$R1FILE" "$R2FILE" "$B1FILE" "$B2FILE"
     27     wait
     28 }
     29 
     30 
     31 function sync_providers() {
     32   infile="$1"
     33   outfile="$2"
     34   echo "Synchronizing providers"
     35   # Sync with providers (up to 3 providers aren't synced here)
     36   for x in 1 2 3; do
     37     echo "Synchronizing providers (round $x)"
     38     anastasis-reducer sync_providers < "$infile" > "$outfile" 2> /dev/null || true
     39     CODE=$(jq -r -e ".code // 0" < $outfile)
     40     # ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED
     41     # FIXME: Temporary workaround for C reducer. See #7227.
     42     if [ "$CODE" = "8420" ]
     43     then
     44       # restore previous non-error state
     45       cp "$infile" "$outfile"
     46       break
     47     fi
     48     # ANASTASIS_REDUCER_ACTION_INVALID
     49     if [ "$CODE" = "8400" ]
     50     then
     51       # restore previous non-error state
     52       cp "$infile" "$outfile"
     53       break
     54     fi
     55     if [ "$CODE" != "0" ]
     56     then
     57       exit_fail "Expected no error or 8420/8400, got $CODE"
     58     fi
     59     cp "$outfile" "$infile"
     60   done
     61   echo "Providers synced."
     62 }
     63 
     64 
     65 # Install cleanup handler (except for kill -9)
     66 trap cleanup EXIT
     67 
     68 
     69 # Transfer funds from the customer's account to the Anastasis account.
     70 # $1 = amount ($CURRENCY:X.Y), $2 = subject.
     71 function wire_transfer_to_anastasis() {
     72   echo -n "Initiating wire transfer ..."
     73   curl -s -X POST \
     74        --user "${DEBIT_USERNAME}:${DEBIT_PASSWORD}" \
     75        -H "Content-Type: application/json" \
     76        -d "{\"payto_uri\":\"${PAYTO_CREDIT}&message=$(echo -n "$2" | jq -sRr @uri)&amount=$1\"}" \
     77        "${BANK_URL}accounts/${DEBIT_USERNAME}/transactions" \
     78        > libeufin-transfer.out
     79   echo " OK"
     80 }
     81 
     82 # Merely a debug utility: show what Anastasis sees on the credit account.
     83 function see_anastasis_transactions() {
     84   curl -s --user "${CREDIT_USERNAME}:${CREDIT_PASSWORD}" \
     85        "${BANK_URL}accounts/${CREDIT_USERNAME}/taler-revenue/history?limit=5" | jq
     86 }
     87 
     88 # Configuration file will be edited, so we create one
     89 # from the template.
     90 CONF=$(mktemp test_free_reducerXXXXXX.conf)
     91 cp test_free_reducer.conf "$CONF"
     92 BANK_CONF=$(mktemp test_iban_bankXXXXXX.conf)
     93 
     94 
     95 
     96 B1FILE=$(mktemp test_reducer_stateB1XXXXXX)
     97 B2FILE=$(mktemp test_reducer_stateB2XXXXXX)
     98 R1FILE=$(mktemp test_reducer_stateR1XXXXXX)
     99 R2FILE=$(mktemp test_reducer_stateR2XXXXXX)
    100 
    101 export CONF
    102 export B2FILE
    103 export B1FILE
    104 export R2FILE
    105 export R1FILE
    106 
    107 echo -n "Testing for libeufin-bank"
    108 libeufin-bank --help > /dev/null || exit_skip "libeufin-bank required"
    109 echo " FOUND"
    110 
    111 echo -n "Testing for curl"
    112 curl --version > /dev/null || exit_skip "curl required"
    113 echo " FOUND"
    114 
    115 # Check we can actually run
    116 echo -n "Testing for jq"
    117 jq -h > /dev/null || exit_skip "jq required"
    118 echo " FOUND"
    119 echo -n "Testing for anastasis-reducer ..."
    120 anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
    121 echo " FOUND"
    122 
    123 echo -n "Initialize Anastasis database ..."
    124 # Name of the Postgres database we will use for the script.
    125 # Will be dropped, do NOT use anything that might be used
    126 # elsewhere
    127 
    128 TARGET_DB=$(anastasis-config -c "$CONF" -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///")
    129 
    130 dropdb "$TARGET_DB" >/dev/null 2>/dev/null || true
    131 createdb "$TARGET_DB" || exit_skip "Could not create database $TARGET_DB"
    132 anastasis-dbinit -c "$CONF" 2> anastasis-dbinit.log
    133 
    134 echo " OK"
    135 
    136 
    137 CURRENCY="EUR"
    138 BANK_DB=anastasisbankcheck
    139 BANK_PORT=8092
    140 BANK_URL="http://localhost:${BANK_PORT}/"
    141 # Both IBANs have to pass the IBAN checksum, libeufin-bank rejects the rest.
    142 export IBAN_CREDIT="DE89370400440532013000"
    143 export IBAN_DEBIT="FR1420041010050500013M02606"
    144 PERSON_CREDIT_NAME="Person Credit"
    145 CREDIT_USERNAME=anastasis-credit-user
    146 CREDIT_PASSWORD=anastasis-credit-password
    147 DEBIT_USERNAME=anastasis-debit-user
    148 DEBIT_PASSWORD=anastasis-debit-password
    149 PAYTO_CREDIT="payto://iban/${IBAN_CREDIT}?receiver-name=Person+Credit"
    150 PAYTO_DEBIT="payto://iban/${IBAN_DEBIT}?receiver-name=Person+Debit"
    151 
    152 echo -n "Configuring bank ..."
    153 cat > "$BANK_CONF" <<EOF
    154 # Generated by test_iban.sh
    155 [libeufin-bank]
    156 CURRENCY = ${CURRENCY}
    157 BASE_URL = ${BANK_URL}
    158 SERVE = tcp
    159 PORT = ${BANK_PORT}
    160 WIRE_TYPE = iban
    161 IBAN_PAYTO_BIC = SANDBOXX
    162 DEFAULT_CUSTOMER_DEBT_LIMIT = ${CURRENCY}:200
    163 DEFAULT_ADMIN_DEBT_LIMIT = ${CURRENCY}:2000
    164 ALLOW_REGISTRATION = YES
    165 # keep the test fast
    166 PWD_HASH_CONFIG = { "cost": 4 }
    167 # the Anastasis IBAN helper authenticates with HTTP basic auth
    168 PWD_AUTH_COMPAT = yes
    169 
    170 [libeufin-bankdb-postgres]
    171 CONFIG = postgres:///${BANK_DB}
    172 EOF
    173 dropdb "$BANK_DB" >/dev/null 2>/dev/null || true
    174 createdb "$BANK_DB" || exit_skip "Could not create database $BANK_DB"
    175 libeufin-bank dbinit -c "$BANK_CONF" &> libeufin-bank-dbinit.log
    176 echo " OK"
    177 
    178 echo -n "Starting bank ..."
    179 libeufin-bank serve -c "$BANK_CONF" &> libeufin-bank.log &
    180 OK=0
    181 for n in $(seq 1 50)
    182 do
    183     sleep 0.2
    184     wget --tries=1 --timeout=1 "${BANK_URL}config" \
    185          -o /dev/null -O /dev/null >/dev/null || continue
    186     OK=1
    187     break
    188 done
    189 if [ 1 != $OK ]
    190 then
    191     exit_skip "Could not launch libeufin-bank"
    192 fi
    193 echo " OK"
    194 
    195 echo -n "Preparing accounts ..."
    196 libeufin-bank create-account -c "$BANK_CONF" \
    197               -u "$CREDIT_USERNAME" -p "$CREDIT_PASSWORD" \
    198               --name "$PERSON_CREDIT_NAME" \
    199               --payto_uri "$PAYTO_CREDIT" > /dev/null
    200 libeufin-bank create-account -c "$BANK_CONF" \
    201               -u "$DEBIT_USERNAME" -p "$DEBIT_PASSWORD" \
    202               --name "Person Debit" \
    203               --payto_uri "$PAYTO_DEBIT" > /dev/null
    204 # the customer has to be able to send money it does not have
    205 libeufin-bank edit-account -c "$BANK_CONF" \
    206               --debit_threshold="${CURRENCY}:1000" \
    207               "$DEBIT_USERNAME" > /dev/null
    208 echo " OK"
    209 
    210 echo -n "Configuring Anastasis IBAN account ..."
    211 anastasis-config -c "$CONF" \
    212                  -s authorization-iban \
    213                  -o CURRENCY \
    214                  -V "${CURRENCY}"
    215 anastasis-config -c "$CONF" \
    216                  -s authorization-iban \
    217                  -o CREDIT_IBAN \
    218                  -V "${IBAN_CREDIT}"
    219 anastasis-config -c "$CONF" \
    220                  -s authorization-iban \
    221                  -o BUSINESS_NAME \
    222                  -V "${PERSON_CREDIT_NAME}"
    223 anastasis-config -c "$CONF" \
    224                  -s authorization-iban \
    225                  -o WIRE_GATEWAY_URL \
    226                  -V "${BANK_URL}accounts/${CREDIT_USERNAME}/taler-revenue/"
    227 anastasis-config -c "$CONF" \
    228                  -s authorization-iban \
    229                  -o WIRE_GATEWAY_AUTH_METHOD \
    230                  -V "basic"
    231 anastasis-config -c "$CONF" \
    232                  -s authorization-iban \
    233                  -o USERNAME \
    234                  -V "${CREDIT_USERNAME}"
    235 anastasis-config -c "$CONF" \
    236                  -s authorization-iban \
    237                  -o PASSWORD \
    238                  -V "${CREDIT_PASSWORD}"
    239 echo " OK"
    240 
    241 echo -n "Launching Anastasis service ..."
    242 PREFIX="" #valgrind
    243 $PREFIX anastasis-httpd -c "$CONF" -L INFO 2> anastasis-httpd_1.log &
    244 echo " OK"
    245 
    246 echo -n "Waiting for Anastasis service ..."
    247 # Wait for Anastasis service to be available
    248 for n in $(seq 1 50)
    249 do
    250     echo -n "."
    251     sleep 0.1
    252     OK=0
    253    # anastasis_01
    254     wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue
    255     OK=1
    256     break
    257 done
    258 if [ 1 != $OK ]
    259 then
    260     exit_skip "Failed to launch Anastasis service"
    261 fi
    262 echo "OK"
    263 
    264 echo -n "Running backup logic ...,"
    265 anastasis-reducer -b > "$B1FILE"
    266 echo -n "."
    267 anastasis-reducer -a \
    268   '{"continent": "Demoworld"}' \
    269   select_continent < "$B1FILE" > "$B2FILE"
    270 echo -n "."
    271 anastasis-reducer -a \
    272   '{"country_code": "xx" }' \
    273   select_country < "$B2FILE" > "$B1FILE" 2>> test_reducer.err
    274 echo -n "."
    275 
    276 anastasis-reducer -a \
    277   '{"identity_attributes": {
    278     "full_name": "Max Musterman",
    279     "sq_number": "4",
    280     "birthdate": "2000-01-01"}}' \
    281   enter_user_attributes < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    282 echo -n ","
    283 cat "$B2FILE" > "$B1FILE"
    284 sync_providers "$B1FILE" "$B2FILE"
    285 echo -n ","
    286 BASEIBAN=$(echo -n $IBAN_DEBIT | gnunet-base32)
    287 anastasis-reducer -a \
    288   "$(jq -n '{ authentication_method: {
    289     type: "iban",
    290     instructions: "Send me your money!",
    291     challenge: $CHALLENGE
    292     } }' \
    293    --arg CHALLENGE "$BASEIBAN"
    294   )" \
    295   add_authentication < "$B2FILE" > "$B1FILE" 2>> test_reducer.err
    296 echo -n "."
    297 
    298 # "91GPWWR" encodes "Hans"
    299 anastasis-reducer -a \
    300   '{"authentication_method": {
    301     "type": "question",
    302     "instructions": "What is your name?",
    303     "challenge": "91GPWWR"
    304     } }' \
    305   add_authentication < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    306 echo -n "."
    307 
    308 mv "$B2FILE" "$B1FILE"
    309 
    310 # Finished adding authentication methods
    311 anastasis-reducer \
    312     next < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    313 
    314 echo -n ","
    315 # Finished policy review
    316 anastasis-reducer \
    317   next < "$B2FILE" > "$B1FILE" 2>> test_reducer.err
    318 echo -n "."
    319 # Note: 'secret' must here be a Crockford base32-encoded value
    320 anastasis-reducer -a \
    321   '{"secret": { "value" : "VERYHARDT0GVESSSECRET", "mime" : "text/plain" }}' \
    322   enter_secret < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    323 mv "$B2FILE" "$B1FILE"
    324 anastasis-reducer next < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    325 echo " OK"
    326 
    327 echo -n "Final backup checks ..."
    328 STATE=$(jq -r -e .backup_state < "$B2FILE")
    329 if [ "$STATE" != "BACKUP_FINISHED" ]
    330 then
    331     exit_fail "Expected new state to be 'BACKUP_FINISHED', got '$STATE'"
    332 fi
    333 jq -r -e .core_secret < "$B2FILE" > /dev/null && exit_fail "'core_secret' was not cleared upon success"
    334 echo " OK"
    335 
    336 echo -n "Running recovery basic logic ..."
    337 anastasis-reducer -r > "$R1FILE"
    338 anastasis-reducer -a \
    339   '{"continent": "Demoworld"}' \
    340   select_continent < "$R1FILE" > "$R2FILE"
    341 anastasis-reducer -a \
    342   '{"country_code": "xx",
    343     "currencies":["TESTKUDOS"]}' \
    344   select_country < "$R2FILE" > "$R1FILE" 2>> test_reducer.err
    345 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
    346 
    347 
    348 STATE=$(jq -r -e .recovery_state < "$R2FILE")
    349 if [ "$STATE" != "SECRET_SELECTING" ]
    350 then
    351     exit_fail "Expected new state to be 'SECRET_SELECTING', got '$STATE'"
    352 fi
    353 echo " OK"
    354 
    355 echo -n "Adding provider (to ensure it is loaded)"
    356 anastasis-reducer -a '{"provider_url" : "http://localhost:8086/" }' add_provider < "$R2FILE" > "$R1FILE"
    357 echo " OK"
    358 
    359 echo -n "Selecting secret to recover"
    360 anastasis-reducer  -a '{"attribute_mask": 0, "providers" : [ { "version": 1, "url" : "http://localhost:8086/" } ] }' \
    361   select_version < "$R1FILE" > "$R2FILE" 2>> test_reducer.err
    362 
    363 STATE=$(jq -r -e .recovery_state < "$R2FILE")
    364 if [ "$STATE" != "CHALLENGE_SELECTING" ]
    365 then
    366     exit_fail "Expected new state to be 'CHALLENGE_SELECTING', got '$STATE'"
    367 fi
    368 echo " OK"
    369 
    370 cp "$R2FILE" "$R1FILE"
    371 sync_providers "$R1FILE" "$R2FILE"
    372 
    373 echo -n "Running challenge selection logic ..."
    374 
    375 UUID0=$(jq -r -e .recovery_information.challenges[0].uuid < "$R2FILE")
    376 UUID1=$(jq -r -e .recovery_information.challenges[1].uuid < "$R2FILE")
    377 UUID0Q=$(jq -r -e .recovery_information.challenges[0].instructions < "$R2FILE")
    378 UUID1Q=$(jq -r -e .recovery_information.challenges[1].instructions < "$R2FILE")
    379 
    380 if [ "$UUID1Q" = 'What is your name?' ]
    381 then
    382     NAME_UUID=$UUID1
    383     IBAN_UUID=$UUID0
    384 else
    385     NAME_UUID=$UUID0
    386     IBAN_UUID=$UUID1
    387 fi
    388 
    389 echo "OK"
    390 echo -n "Solving first challenge ..."
    391 anastasis-reducer -a \
    392   "$(jq -n '
    393     {
    394         uuid: $UUID
    395     }' \
    396     --arg UUID "$NAME_UUID"
    397   )" \
    398   select_challenge < "$R2FILE" > "$R1FILE" 2>> test_reducer.err
    399 
    400 anastasis-reducer -a '{"answer": "Hans"}' \
    401   solve_challenge < "$R1FILE" > "$R2FILE"
    402 
    403 echo "OK"
    404 echo -n "Solving IBAN challenge ..."
    405 
    406 anastasis-reducer -a \
    407   "$(jq -n '
    408     {
    409         uuid: $UUID
    410     }' \
    411     --arg UUID "$IBAN_UUID"
    412   )" \
    413   select_challenge < "$R2FILE" > "$R1FILE" 2>> test_reducer.err
    414 echo "OK"
    415 
    416 
    417 METHOD=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".state < "$R1FILE")
    418 if [ "$METHOD" != "iban-instructions" ]
    419 then
    420     exit_fail "Expected method to be 'iban-instructions', got ${METHOD}"
    421 fi
    422 
    423 ACC=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".target_iban < "$R1FILE")
    424 if [ "$ACC" != "${IBAN_CREDIT}" ]
    425 then
    426     exit_fail "Expected account to be ${IBAN_CREDIT}, got ${ACC}"
    427 fi
    428 
    429 anastasis-reducer \
    430   back < "$R1FILE" > "$R2FILE" 2>> test_reducer.err
    431 
    432 
    433 AMOUNT=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".challenge_amount < "$R1FILE")
    434 SUBJECT=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".wire_transfer_subject < "$R1FILE")
    435 
    436 echo -n "Performing authorization wire transfer ${SUBJECT} ..."
    437 wire_transfer_to_anastasis "${AMOUNT}" "${SUBJECT}"
    438 
    439 echo " OK"
    440 
    441 echo -n "Triggering inbound check ..."
    442 anastasis-helper-authorization-iban -c "$CONF" -t -L INFO
    443 echo " OK"
    444 
    445 # Now we should get the secret...
    446 echo -n "Polling for recovery ..."
    447 anastasis-reducer poll -L INFO < "$R2FILE" > "$R1FILE"
    448 echo " OK"
    449 
    450 echo -n "Checking recovered secret ..."
    451 # finally: check here that we recovered the secret...
    452 
    453 STATE=$(jq -r -e .recovery_state < "$R1FILE")
    454 if [ "$STATE" != "RECOVERY_FINISHED" ]
    455 then
    456     jq -e . "$R1FILE"
    457     exit_fail "Expected new state to be 'RECOVERY_FINISHED', got '$STATE'"
    458 fi
    459 
    460 SECRET=$(jq -r -e .core_secret.value < "$R1FILE")
    461 if [ "$SECRET" != "VERYHARDT0GVESSSECRET" ]
    462 then
    463     jq -e . "$R1FILE"
    464     exit_fail "Expected recovered secret to be 'VERYHARDT0GVESSSECRET', got '$SECRET'"
    465 fi
    466 
    467 MIME=$(jq -r -e .core_secret.mime < "$R1FILE")
    468 if [ "$MIME" != "text/plain" ]
    469 then
    470     jq -e . "$R1FILE"
    471     exit_fail "Expected recovered mime to be 'text/plain', got '$MIME'"
    472 fi
    473 
    474 echo " OK"
    475 
    476 exit 0