anastasis

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

test_iban.sh (16021B)


      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" "$R1FILE" "$R2FILE" "$B1FILE" "$B2FILE"
     27     wait
     28 }
     29 
     30 
     31 # $1=ebics username, $2=ebics partner name, $3=person name, $4=sandbox bank account name, $5=iban
     32 function prepare_sandbox_account() {
     33   echo -n "Registering $4 to the Sandbox..."
     34   export LIBEUFIN_SANDBOX_USERNAME="$4"
     35   export LIBEUFIN_SANDBOX_PASSWORD=unused
     36   libeufin-cli sandbox --sandbox-url="$SANDBOX_URL" \
     37     demobank register --name "$3" --iban "$5"
     38   echo " OK"
     39   echo -n "Associating a EBICS subscriber to $4..."
     40   export LIBEUFIN_SANDBOX_USERNAME=admin
     41   libeufin-cli sandbox --sandbox-url="$SANDBOX_URL" demobank new-ebicssubscriber \
     42     --host-id "$EBICS_HOST" \
     43     --user-id "$1" --partner-id "$2" \
     44     --bank-account "$4" # that's a username _and_ a bank account name
     45   echo " OK"
     46 
     47   unset LIBEUFIN_SANDBOX_USERNAME
     48   unset LIBEUFIN_SANDBOX_PASSWORD
     49 }
     50 
     51 function sync_providers() {
     52   infile="$1"
     53   outfile="$2"
     54   echo "Synchronizing providers"
     55   # Sync with providers (up to 3 providers aren't synced here)
     56   for x in 1 2 3; do
     57     echo "Synchronizing providers (round $x)"
     58     anastasis-reducer sync_providers < "$infile" > "$outfile" 2> /dev/null || true
     59     CODE=$(jq -r -e ".code // 0" < $outfile)
     60     # ANASTASIS_REDUCER_PROVIDERS_ALREADY_SYNCED
     61     # FIXME: Temporary workaround for C reducer. See #7227.
     62     if [ "$CODE" = "8420" ]
     63     then
     64       # restore previous non-error state
     65       cp "$infile" "$outfile"
     66       break
     67     fi
     68     # ANASTASIS_REDUCER_ACTION_INVALID
     69     if [ "$CODE" = "8400" ]
     70     then
     71       # restore previous non-error state
     72       cp "$infile" "$outfile"
     73       break
     74     fi
     75     if [ "$CODE" != "0" ]
     76     then
     77       exit_fail "Expected no error or 8420/8400, got $CODE"
     78     fi
     79     cp "$outfile" "$infile"
     80   done
     81   echo "Providers synced."
     82 }
     83 
     84 
     85 # Install cleanup handler (except for kill -9)
     86 trap cleanup EXIT
     87 
     88 
     89 # Transfer only from debit to credit/anastasis account.
     90 # This function moves funds directly at the Sandbox.  No need
     91 # to pass through the Nexus+Ebics layer to issue the payment
     92 # $1 = amount ($CURRENCY:X.Y), $2 = subject.
     93 function wire_transfer_to_anastasis() {
     94   echo -n "Initiating wire transfer ..."
     95   libeufin-sandbox make-transaction \
     96     --debit-account=sandbox-account-debit \
     97     --credit-account=sandbox-account-credit "$1" "$2" &> libeufin-transfer-initiate.out
     98   echo " OK"
     99   # FIXME-MS: the following command reports that it did not
    100   # sync any transactions, even though presumably we just
    101   # made one in the one above (which succeeded...)
    102   echo -n "Syncing nexus with sandbox ..."
    103   export LIBEUFIN_NEXUS_USERNAME="$CREDIT_USERNAME"
    104   export LIBEUFIN_NEXUS_PASSWORD="$CREDIT_PASSWORD"
    105   libeufin-cli accounts fetch-transactions nexus-bankaccount-credit &> libeufin-transfer-fetch.out
    106   echo " OK"
    107 }
    108 
    109 # $1 = facade base URL.  Merely a debug utility.
    110 function see_anastasis_transactions_via_facade() {
    111   curl -s --user "$CREDIT_USERNAME:$CREDIT_PASSWORD" "${1}history/incoming?delta=5" | jq
    112 }
    113 
    114 # $1 = ebics user id, $2 = ebics partner, $3 = bank connection name
    115 # $4 = bank account name local to Nexus, $5 = bank account name as known
    116 # by Sandbox
    117 function prepare_nexus_account() {
    118   echo -n "Making bank connection $3 ..."
    119   libeufin-cli connections new-ebics-connection \
    120     --ebics-url="${SANDBOX_URL}ebicsweb" \
    121     --host-id="$EBICS_HOST" \
    122     --partner-id="$2" \
    123     --ebics-user-id="$1" \
    124     $3 > /dev/null
    125   echo " OK"
    126   echo -n "Connecting $3 ..."
    127   libeufin-cli connections connect "$3" > /dev/null
    128   echo " OK"
    129   echo -n "Importing Sandbox bank account ($5) to Nexus ($4) ..."
    130   libeufin-cli connections download-bank-accounts "$3" > /dev/null
    131   libeufin-cli connections import-bank-account \
    132     --offered-account-id="$5" --nexus-bank-account-id="$4" "$3" > /dev/null
    133   echo " OK"
    134 }
    135 
    136 
    137 # Configuration file will be edited, so we create one
    138 # from the template.
    139 CONF=$(mktemp test_free_reducerXXXXXX.conf)
    140 cp test_free_reducer.conf "$CONF"
    141 
    142 
    143 
    144 B1FILE=$(mktemp test_reducer_stateB1XXXXXX)
    145 B2FILE=$(mktemp test_reducer_stateB2XXXXXX)
    146 R1FILE=$(mktemp test_reducer_stateR1XXXXXX)
    147 R2FILE=$(mktemp test_reducer_stateR2XXXXXX)
    148 
    149 export CONF
    150 export B2FILE
    151 export B1FILE
    152 export R2FILE
    153 export R1FILE
    154 
    155 echo -n "Testing for libeufin-cli"
    156 libeufin-cli --version > /dev/null || exit_skip "libeufin-cli required"
    157 echo " FOUND"
    158 
    159 echo -n "Testing for libeufin-nexus"
    160 libeufin-nexus --version > /dev/null || exit_skip "libeufin-nexus required"
    161 echo " FOUND"
    162 
    163 echo -n "Testing for libeufin-sandbox"
    164 libeufin-sandbox --version > /dev/null || exit_skip "libeufin-sandbox required"
    165 echo " FOUND"
    166 
    167 # Check we can actually run
    168 echo -n "Testing for jq"
    169 jq -h > /dev/null || exit_skip "jq required"
    170 echo " FOUND"
    171 echo -n "Testing for anastasis-reducer ..."
    172 anastasis-reducer -h > /dev/null || exit_skip "anastasis-reducer required"
    173 echo " FOUND"
    174 
    175 echo -n "Initialize Anastasis database ..."
    176 # Name of the Postgres database we will use for the script.
    177 # Will be dropped, do NOT use anything that might be used
    178 # elsewhere
    179 
    180 TARGET_DB=$(anastasis-config -c "$CONF" -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///")
    181 
    182 dropdb "$TARGET_DB" >/dev/null 2>/dev/null || true
    183 createdb "$TARGET_DB" || exit_skip "Could not create database $TARGET_DB"
    184 anastasis-dbinit -c "$CONF" 2> anastasis-dbinit.log
    185 
    186 echo " OK"
    187 
    188 
    189 export LIBEUFIN_NEXUS_DB_CONNECTION="postgres:///anastasischeck"
    190 export LIBEUFIN_SANDBOX_DB_CONNECTION="postgres:///anastasischeck"
    191 NEXUS_URL="http://localhost:5001/"
    192 SANDBOX_URL="http://localhost:5000/"
    193 
    194 echo -n "Starting Nexus ..."
    195 libeufin-nexus serve &> nexus.log &
    196 nexus_pid=$!
    197 if ! curl -s --retry 5 --retry-connrefused "$NEXUS_URL" > /dev/null; then
    198   exit_skip "Could not launch Nexus"
    199 fi
    200 echo " OK"
    201 
    202 echo -n "Configuring Sandbox..."
    203 libeufin-sandbox config default &> sandbox-config.log
    204 echo " OK"
    205 echo -n "Starting Sandbox ..."
    206 libeufin-sandbox serve --no-auth &> sandbox-serve.log &
    207 sandbox_pid=$!
    208 if ! curl -s --retry 5 --retry-connrefused "$SANDBOX_URL" > /dev/null; then
    209   exit_skip "Could not launch Sandbox"
    210 fi
    211 echo " OK"
    212 
    213 CURRENCY="EUR"
    214 # CURRENCY="TESTKUDOS"
    215 
    216 EBICS_HOST="ebicstesthost"
    217 export IBAN_CREDIT="DE89370400440532013000"
    218 export IBAN_DEBIT="FR1420041010050500013M02606"
    219 
    220 echo -n "Preparing Sandbox (creating the EBICS host) ..."
    221 libeufin-cli \
    222   sandbox --sandbox-url="$SANDBOX_URL" \
    223     ebicshost create \
    224       --host-id="$EBICS_HOST"
    225 echo " OK"
    226 
    227 PERSON_CREDIT_NAME="Person Credit"
    228 echo -n "Preparing accounts ..."
    229 # note: Ebisc schema doesn't allow dashed names.
    230 prepare_sandbox_account \
    231   ebicsuserCredit \
    232   ebicspartnerCredit \
    233   "${PERSON_CREDIT_NAME}" \
    234   sandbox-account-credit \
    235   "$IBAN_CREDIT"
    236 prepare_sandbox_account \
    237   ebicsuserDebit \
    238   ebicspartnerDebit \
    239   "Person Debit" \
    240   sandbox-account-debit \
    241   "$IBAN_DEBIT"
    242 echo "Sandbox preparation done"
    243 echo -n "Preparing Nexus ..."
    244 export LIBEUFIN_NEXUS_URL="$NEXUS_URL"
    245 # Make debit user, will buy Anastasis services.
    246 DEBIT_USERNAME=anastasis-debit-user
    247 DEBIT_PASSWORD=anastasis-debit-password
    248 libeufin-nexus superuser "$DEBIT_USERNAME" --password="$DEBIT_PASSWORD"
    249 echo " OK"
    250 export LIBEUFIN_NEXUS_USERNAME="$DEBIT_USERNAME"
    251 export LIBEUFIN_NEXUS_PASSWORD="$DEBIT_PASSWORD"
    252 
    253 # Make credit user, will be Anastasis client.
    254 CREDIT_USERNAME=anastasis-credit-user
    255 CREDIT_PASSWORD=anastasis-credit-password
    256 echo -n "Create credit user (for anastasis) at Nexus ..."
    257 libeufin-nexus superuser "$CREDIT_USERNAME" --password="$CREDIT_PASSWORD"
    258 echo " OK"
    259 export LIBEUFIN_NEXUS_USERNAME="$CREDIT_USERNAME"
    260 export LIBEUFIN_NEXUS_PASSWORD="$CREDIT_PASSWORD"
    261 
    262 prepare_nexus_account \
    263   ebicsuserCredit \
    264   ebicspartnerCredit \
    265   bankconnection-credit \
    266   nexus-bankaccount-credit \
    267   sandbox-account-credit
    268 
    269 echo -n "Create facade ..."
    270 libeufin-cli facades new-anastasis-facade \
    271   --currency="$CURRENCY" \
    272   --facade-name=facade-credit \
    273   bankconnection-credit nexus-bankaccount-credit
    274 echo " OK"
    275 FACADE_URL=$(libeufin-cli facades list | jq .facades[0].baseUrl | tr -d \")
    276 
    277 ## Reach facade with: $FACADE_URL + $CREDIT_USERNAME + $CREDIT_PASSWORD
    278 
    279 
    280 echo -n "Configuring Anastasis IBAN account ..."
    281 anastasis-config -c "$CONF" \
    282                  -s authorization-iban \
    283                  -o CREDIT_IBAN \
    284                  -V "${IBAN_CREDIT}"
    285 anastasis-config -c "$CONF" \
    286                  -s authorization-iban \
    287                  -o BUSINESS_NAME \
    288                  -V "${PERSON_CREDIT_NAME}"
    289 anastasis-config -c "$CONF" \
    290                  -s authorization-iban \
    291                  -o WIRE_GATEWAY_URL \
    292                  -V "${FACADE_URL}"
    293 anastasis-config -c "$CONF" \
    294                  -s authorization-iban \
    295                  -o WIRE_GATEWAY_AUTH_METHOD \
    296                  -V "basic"
    297 anastasis-config -c "$CONF" \
    298                  -s authorization-iban \
    299                  -o USERNAME \
    300                  -V "${LIBEUFIN_NEXUS_USERNAME}"
    301 anastasis-config -c "$CONF" \
    302                  -s authorization-iban \
    303                  -o PASSWORD \
    304                  -V "${LIBEUFIN_NEXUS_PASSWORD}"
    305 echo " OK"
    306 
    307 echo -n "Launching Anastasis service ..."
    308 PREFIX="" #valgrind
    309 $PREFIX anastasis-httpd -c "$CONF" -L INFO 2> anastasis-httpd_1.log &
    310 echo " OK"
    311 
    312 echo -n "Waiting for Anastasis service ..."
    313 # Wait for Anastasis service to be available
    314 for n in $(seq 1 50)
    315 do
    316     echo -n "."
    317     sleep 0.1
    318     OK=0
    319    # anastasis_01
    320     wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue
    321     OK=1
    322     break
    323 done
    324 if [ 1 != $OK ]
    325 then
    326     exit_skip "Failed to launch Anastasis service"
    327 fi
    328 echo "OK"
    329 
    330 echo -n "Running backup logic ...,"
    331 anastasis-reducer -b > "$B1FILE"
    332 echo -n "."
    333 anastasis-reducer -a \
    334   '{"continent": "Demoworld"}' \
    335   select_continent < "$B1FILE" > "$B2FILE"
    336 echo -n "."
    337 anastasis-reducer -a \
    338   '{"country_code": "xx" }' \
    339   select_country < "$B2FILE" > "$B1FILE" 2>> test_reducer.err
    340 echo -n "."
    341 
    342 anastasis-reducer -a \
    343   '{"identity_attributes": {
    344     "full_name": "Max Musterman",
    345     "sq_number": "4",
    346     "birthdate": "2000-01-01"}}' \
    347   enter_user_attributes < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    348 echo -n ","
    349 cat "$B2FILE" > "$B1FILE"
    350 sync_providers "$B1FILE" "$B2FILE"
    351 echo -n ","
    352 BASEIBAN=$(echo -n $IBAN_DEBIT | gnunet-base32)
    353 anastasis-reducer -a \
    354   "$(jq -n '{ authentication_method: {
    355     type: "iban",
    356     instructions: "Send me your money!",
    357     challenge: $CHALLENGE
    358     } }' \
    359    --arg CHALLENGE "$BASEIBAN"
    360   )" \
    361   add_authentication < "$B2FILE" > "$B1FILE" 2>> test_reducer.err
    362 echo -n "."
    363 
    364 # "91GPWWR" encodes "Hans"
    365 anastasis-reducer -a \
    366   '{"authentication_method": {
    367     "type": "question",
    368     "instructions": "What is your name?",
    369     "challenge": "91GPWWR"
    370     } }' \
    371   add_authentication < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    372 echo -n "."
    373 
    374 mv "$B2FILE" "$B1FILE"
    375 
    376 # Finished adding authentication methods
    377 anastasis-reducer \
    378     next < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    379 
    380 echo -n ","
    381 # Finished policy review
    382 anastasis-reducer \
    383   next < "$B2FILE" > "$B1FILE" 2>> test_reducer.err
    384 echo -n "."
    385 # Note: 'secret' must here be a Crockford base32-encoded value
    386 anastasis-reducer -a \
    387   '{"secret": { "value" : "VERYHARDT0GVESSSECRET", "mime" : "text/plain" }}' \
    388   enter_secret < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    389 mv "$B2FILE" "$B1FILE"
    390 anastasis-reducer next < "$B1FILE" > "$B2FILE" 2>> test_reducer.err
    391 echo " OK"
    392 
    393 echo -n "Final backup checks ..."
    394 STATE=$(jq -r -e .backup_state < "$B2FILE")
    395 if [ "$STATE" != "BACKUP_FINISHED" ]
    396 then
    397     exit_fail "Expected new state to be 'BACKUP_FINISHED', got '$STATE'"
    398 fi
    399 jq -r -e .core_secret < "$B2FILE" > /dev/null && exit_fail "'core_secret' was not cleared upon success"
    400 echo " OK"
    401 
    402 echo -n "Running recovery basic logic ..."
    403 anastasis-reducer -r > "$R1FILE"
    404 anastasis-reducer -a \
    405   '{"continent": "Demoworld"}' \
    406   select_continent < "$R1FILE" > "$R2FILE"
    407 anastasis-reducer -a \
    408   '{"country_code": "xx",
    409     "currencies":["TESTKUDOS"]}' \
    410   select_country < "$R2FILE" > "$R1FILE" 2>> test_reducer.err
    411 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
    412 
    413 
    414 STATE=$(jq -r -e .recovery_state < "$R2FILE")
    415 if [ "$STATE" != "SECRET_SELECTING" ]
    416 then
    417     exit_fail "Expected new state to be 'SECRET_SELECTING', got '$STATE'"
    418 fi
    419 echo " OK"
    420 
    421 echo -n "Adding provider (to ensure it is loaded)"
    422 anastasis-reducer -a '{"provider_url" : "http://localhost:8086/" }' add_provider < "$R2FILE" > "$R1FILE"
    423 echo " OK"
    424 
    425 echo -n "Selecting secret to recover"
    426 anastasis-reducer  -a '{"attribute_mask": 0, "providers" : [ { "version": 1, "url" : "http://localhost:8086/" } ] }' \
    427   select_version < "$R1FILE" > "$R2FILE" 2>> test_reducer.err
    428 
    429 STATE=$(jq -r -e .recovery_state < "$R2FILE")
    430 if [ "$STATE" != "CHALLENGE_SELECTING" ]
    431 then
    432     exit_fail "Expected new state to be 'CHALLENGE_SELECTING', got '$STATE'"
    433 fi
    434 echo " OK"
    435 
    436 cp "$R2FILE" "$R1FILE"
    437 sync_providers "$R1FILE" "$R2FILE"
    438 
    439 echo -n "Running challenge selection logic ..."
    440 
    441 UUID0=$(jq -r -e .recovery_information.challenges[0].uuid < "$R2FILE")
    442 UUID1=$(jq -r -e .recovery_information.challenges[1].uuid < "$R2FILE")
    443 UUID0Q=$(jq -r -e .recovery_information.challenges[0].instructions < "$R2FILE")
    444 UUID1Q=$(jq -r -e .recovery_information.challenges[1].instructions < "$R2FILE")
    445 
    446 if [ "$UUID1Q" = 'What is your name?' ]
    447 then
    448     NAME_UUID=$UUID1
    449     IBAN_UUID=$UUID0
    450 else
    451     NAME_UUID=$UUID0
    452     IBAN_UUID=$UUID1
    453 fi
    454 
    455 echo "OK"
    456 echo -n "Solving first challenge ..."
    457 anastasis-reducer -a \
    458   "$(jq -n '
    459     {
    460         uuid: $UUID
    461     }' \
    462     --arg UUID "$NAME_UUID"
    463   )" \
    464   select_challenge < "$R2FILE" > "$R1FILE" 2>> test_reducer.err
    465 
    466 anastasis-reducer -a '{"answer": "Hans"}' \
    467   solve_challenge < "$R1FILE" > "$R2FILE"
    468 
    469 echo "OK"
    470 echo -n "Solving IBAN challenge ..."
    471 
    472 anastasis-reducer -a \
    473   "$(jq -n '
    474     {
    475         uuid: $UUID
    476     }' \
    477     --arg UUID "$IBAN_UUID"
    478   )" \
    479   select_challenge < "$R2FILE" > "$R1FILE" 2>> test_reducer.err
    480 echo "OK"
    481 
    482 
    483 METHOD=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".state < "$R1FILE")
    484 if [ "$METHOD" != "iban-instructions" ]
    485 then
    486     exit_fail "Expected method to be 'iban-instructions', got ${METHOD}"
    487 fi
    488 
    489 ACC=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".target_iban < "$R1FILE")
    490 if [ "$ACC" != "${IBAN_CREDIT}" ]
    491 then
    492     exit_fail "Expected account to be ${IBAN_CREDIT}, got ${ACC}"
    493 fi
    494 
    495 anastasis-reducer \
    496   back < "$R1FILE" > "$R2FILE" 2>> test_reducer.err
    497 
    498 
    499 AMOUNT=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".challenge_amount < "$R1FILE")
    500 SUBJECT=$(jq -r -e .challenge_feedback.\"$IBAN_UUID\".wire_transfer_subject < "$R1FILE")
    501 
    502 echo -n "Performing authorization wire transfer ${SUBJECT} ..."
    503 wire_transfer_to_anastasis "${AMOUNT}" "${SUBJECT}"
    504 
    505 echo " OK"
    506 
    507 echo -n "Triggering inbound check ..."
    508 anastasis-helper-authorization-iban -c "$CONF" -t -L INFO
    509 echo " OK"
    510 
    511 # Now we should get the secret...
    512 echo -n "Polling for recovery ..."
    513 anastasis-reducer poll -L INFO < "$R2FILE" > "$R1FILE"
    514 echo " OK"
    515 
    516 echo -n "Checking recovered secret ..."
    517 # finally: check here that we recovered the secret...
    518 
    519 STATE=$(jq -r -e .recovery_state < "$R1FILE")
    520 if [ "$STATE" != "RECOVERY_FINISHED" ]
    521 then
    522     jq -e . "$R1FILE"
    523     exit_fail "Expected new state to be 'RECOVERY_FINISHED', got '$STATE'"
    524 fi
    525 
    526 SECRET=$(jq -r -e .core_secret.value < "$R1FILE")
    527 if [ "$SECRET" != "VERYHARDT0GVESSSECRET" ]
    528 then
    529     jq -e . "$R1FILE"
    530     exit_fail "Expected recovered secret to be 'VERYHARDT0GVESSSECRET', got '$SECRET'"
    531 fi
    532 
    533 MIME=$(jq -r -e .core_secret.mime < "$R1FILE")
    534 if [ "$MIME" != "text/plain" ]
    535 then
    536     jq -e . "$R1FILE"
    537     exit_fail "Expected recovered mime to be 'text/plain', got '$MIME'"
    538 fi
    539 
    540 echo " OK"
    541 
    542 exit 0