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