test_prepare.sh (17801B)
1 #!/bin/bash 2 # Shell script to launch Taler components 3 # and Anastasis providers for local test 4 # using TESTKUDOS. 5 6 set -eu 7 8 # Exit, with status code "skip" (no 'real' failure) 9 function exit_skip() { 10 echo " SKIP: $1" 11 exit 77 12 } 13 14 # Exit, with error message (hard failure) 15 function exit_fail() { 16 echo " FAIL: $1" 17 exit 1 18 } 19 20 # Cleanup to run whenever we exit 21 function cleanup() 22 { 23 if test -n "${SETUP_PID:-}" 24 then 25 kill -TERM "$SETUP_PID" 2> /dev/null || true 26 wait "$SETUP_PID" 2> /dev/null || true 27 fi 28 for n in `jobs -p` 29 do 30 kill -SIGCONT $n # in case one provider was suspended 31 kill $n 2> /dev/null || true 32 done 33 rm -rf $CONF $CONF.edited $CONF_4 $CONF_IBAN $WALLET_DB $R1FILE $R2FILE $B1FILE $B2FILE $TMP_DIR 34 wait 35 } 36 37 38 # Launch the GNU Taler services (bank, exchange, merchant) via 39 # taler-unified-setup.sh and return once they are up. Mirrors the setup() 40 # helper of exchange/src/testing/setup.sh; we cannot source that file as it is 41 # not installed. Arguments are passed on to taler-unified-setup.sh. 42 function taler_setup() 43 { 44 local FIFO_DIR 45 local FIFO_OUT 46 47 # Create a named pipe in a temp directory we own. 48 FIFO_DIR=`mktemp -d fifo-XXXXXX` 49 FIFO_OUT="$FIFO_DIR/out" 50 mkfifo "$FIFO_OUT" 51 # Open pipe as FD 3 (RW) and FD 4 (RO) 52 exec 3<> "$FIFO_OUT" 4< "$FIFO_OUT" 53 rm -rf "$FIFO_DIR" 54 # '-W' is required for our termination logic to work. 55 taler-unified-setup.sh -W "$@" >&3 2> taler-unified-setup.log & 56 SETUP_PID=$! 57 exec 3>&- 58 # taler-unified-setup.sh prints "READY:$TESTROOT" once everything is up. 59 # Read until EOF instead of 'sed -u /READY:/ q' so that we can tell "the 60 # system came up" apart from "taler-unified-setup.sh died first". 61 READY=0 62 while IFS= read -r LINE <&4 63 do 64 case "$LINE" in 65 READY:*) 66 READY=1 67 break 68 ;; 69 esac 70 done 71 exec 4>&- 72 if test 1 != $READY 73 then 74 exit_skip "Failed to launch Taler services, see taler-unified-setup.log" 75 fi 76 } 77 78 79 # Create a bank account at libeufin-bank. Prints its payto URI. 80 # $1 = username, $2 = password, $3 = legal name, $4 = payto URI (optional) 81 function make_bank_account() { 82 if test -z "${4:-}" 83 then 84 libeufin-bank create-account \ 85 -c $CONF_IBAN \ 86 -u "$1" -p "$2" --name "$3" 2>> iban-setup.log 87 else 88 libeufin-bank create-account \ 89 -c $CONF_IBAN \ 90 -u "$1" -p "$2" --name "$3" --payto_uri "$4" 2>> iban-setup.log 91 fi 92 } 93 94 95 # Transfer funds from the debit account to the Anastasis account and have 96 # the Anastasis IBAN helper pick the transfer up. 97 # $1 = amount ($CURRENCY:X.Y), $2 = subject. 98 function wire_transfer_to_anastasis() { 99 curl -s -X POST \ 100 --user "${DEBIT_USERNAME}:${DEBIT_PASSWORD}" \ 101 -H "Content-Type: application/json" \ 102 -d "{\"payto_uri\":\"${PAYTO_CREDIT}&message=$(echo "$2" | jq -sRr @uri)&amount=$1\"}" \ 103 "${IBAN_BANK_URL}accounts/${DEBIT_USERNAME}/transactions" > /dev/null 104 anastasis-helper-authorization-iban -c $CONF_4 -t -L INFO 105 } 106 107 108 if test "${1:-}" != "free" -a "${1:-}" != "fees" 109 then 110 echo "Launch script with either 'free' or 'fees' argument to launch providers with/without fees." 111 exit 1 112 fi 113 114 # The provider configurations run helper commands that ship next to this 115 # script (notably 'scat', the [authorization-email] COMMAND). anastasis-httpd 116 # resolves them via PATH, so make sure our own directory is on it. 117 SRCDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 118 export PATH="$SRCDIR:$PATH" 119 120 export CONF_1="test_anastasis_reducer_1.conf" 121 export CONF_2="test_anastasis_reducer_2.conf" 122 export CONF_3="test_anastasis_reducer_3.conf" 123 if test $1 = 'free' 124 then 125 CONF4="test_anastasis_reducer_4_free.conf" 126 else 127 CONF4="test_anastasis_reducer_4.conf" 128 fi 129 130 # Taler configuration file will be edited, so we create one 131 # from the template. 132 export CONF=`mktemp test_reducerXXXXXX.conf` 133 export CONF_4=`mktemp test_reducer_4XXXXXX.conf` 134 export CONF_IBAN=`mktemp test_reducer_ibanXXXXXX.conf` 135 cp test_reducer.conf $CONF 136 cp $CONF4 $CONF_4 137 138 TMP_DIR=`mktemp -d keys-tmp-XXXXXX` 139 # wallet-core needs an sqlite3 database; a '.json' name selects the 140 # no-longer-supported in-memory backend. 141 WALLET_DB=`mktemp test_reducer_walletXXXXXX.sqlite3` 142 B1FILE=`mktemp test_reducer_stateB1XXXXXX` 143 B2FILE=`mktemp test_reducer_stateB2XXXXXX` 144 R1FILE=`mktemp test_reducer_stateR1XXXXXX` 145 R2FILE=`mktemp test_reducer_stateR2XXXXXX` 146 IBAN_ACTIVE='false' 147 SETUP_PID='' 148 149 # Install cleanup handler (except for kill -9) 150 trap cleanup EXIT 151 152 # Check we can actually run 153 if test $1 = 'fees' 154 then 155 echo -n "Testing for taler" 156 taler-exchange-httpd -h > /dev/null || exit_skip " taler-exchange required" 157 taler-merchant-httpd -h > /dev/null || exit_skip " taler-merchant required" 158 # note: 'taler-unified-setup.sh -h' exits non-zero (its exit trap wins) 159 command -v taler-unified-setup.sh > /dev/null || exit_skip " taler-unified-setup.sh required" 160 echo " FOUND" 161 162 echo -n "Testing for libeufin-bank" 163 libeufin-bank --help >/dev/null </dev/null || exit_skip " MISSING" 164 echo " FOUND" 165 166 echo -n "Testing for taler-wallet-cli" 167 taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING" 168 echo " FOUND" 169 170 echo -n "Testing for jq" 171 jq --version >/dev/null </dev/null || exit_skip " MISSING" 172 echo " FOUND" 173 fi 174 175 echo -n "Testing for anastasis-httpd" 176 anastasis-httpd -h >/dev/null </dev/null || exit_skip " MISSING" 177 echo " FOUND" 178 179 echo -n "Initialize anastasis database ..." 180 # Name of the Postgres database we will use for the script. 181 # Will be dropped, do NOT use anything that might be used 182 # elsewhere 183 TARGET_DB_1=`anastasis-config -c $CONF_1 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"` 184 TARGET_DB_2=`anastasis-config -c $CONF_2 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"` 185 TARGET_DB_3=`anastasis-config -c $CONF_3 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"` 186 TARGET_DB_4=`anastasis-config -c $CONF_4 -s stasis-postgres -o CONFIG | sed -e "s/^postgres:\/\/\///"` 187 188 dropdb $TARGET_DB_1 >/dev/null 2>/dev/null || true 189 createdb $TARGET_DB_1 || exit_skip "Could not create database $TARGET_DB_1" 190 anastasis-dbinit -c $CONF_1 2> anastasis-dbinit_1.log 191 dropdb $TARGET_DB_2 >/dev/null 2>/dev/null || true 192 createdb $TARGET_DB_2 || exit_skip "Could not create database $TARGET_DB_2" 193 anastasis-dbinit -c $CONF_2 2> anastasis-dbinit_2.log 194 dropdb $TARGET_DB_3 >/dev/null 2>/dev/null || true 195 createdb $TARGET_DB_3 || exit_skip "Could not create database $TARGET_DB_3" 196 anastasis-dbinit -c $CONF_3 2> anastasis-dbinit_3.log 197 dropdb $TARGET_DB_4 >/dev/null 2>/dev/null || true 198 createdb $TARGET_DB_4 || exit_skip "Could not create database $TARGET_DB_4" 199 anastasis-dbinit -c $CONF_4 2> anastasis-dbinit_4.log 200 201 echo " OK" 202 203 if test $1 = 'fees' 204 then 205 206 echo -n "Setting up Taler databases ..." 207 TALER_DB=talercheck 208 # bank, exchange and merchant share one database here 209 taler-exchange-config -c $CONF -s exchangedb-postgres -o CONFIG -V postgres:///$TALER_DB 210 taler-exchange-config -c $CONF -s merchantdb-postgres -o CONFIG -V postgres:///$TALER_DB 211 taler-exchange-config -c $CONF -s libeufin-bankdb-postgres -o CONFIG -V postgres:///$TALER_DB 212 taler-exchange-config -c $CONF -s exchange -o KEYDIR -V "${TMP_DIR}/keydir/" 213 taler-exchange-config -c $CONF -s exchange -o REVOCATION_DIR -V "${TMP_DIR}/revdir/" 214 DATA_DIR=`taler-exchange-config -f -c $CONF -s PATHS -o TALER_HOME` 215 rm -rf $DATA_DIR 216 dropdb $TALER_DB >/dev/null 2>/dev/null || true 217 createdb $TALER_DB || exit_skip "Could not create database $TALER_DB" 218 echo " OK" 219 220 # taler-unified-setup.sh launches the bank (libeufin-bank), the exchange 221 # (including the secmods and the offline key ceremony) and the merchant 222 # (including taler-merchant-exchangekeyupdate, without which the merchant 223 # never learns the exchange's keys and refuses to create orders). It 224 # writes the configuration it actually uses to "$CONF.edited". 225 echo "Launching Taler services ..." 226 taler_setup -c $CONF -L INFO -b -e -m -w -r merchant-exchange-default 227 echo "Taler services up" 228 229 BANK_PORT=`taler-exchange-config -c $CONF.edited -s libeufin-bank -o PORT` 230 BANK_URL="http://localhost:${BANK_PORT}/" 231 EXCHANGE_URL=`taler-exchange-config -c $CONF.edited -s exchange -o BASE_URL` 232 MERCHANT_PORT=`taler-merchant-config -c $CONF.edited -s merchant -o PORT` 233 MERCHANT_URL="http://localhost:${MERCHANT_PORT}/" 234 235 echo -n "Configuring merchant instance ..." 236 # POST to the merchant and fail loudly if it does not like the request. 237 # $1 = path, $2 = JSON body 238 function merchant_post() { 239 local RESP 240 local CODE 241 242 RESP=`curl -s -w '\n%{http_code}' -H "Content-Type: application/json" \ 243 -X POST -d "$2" "${MERCHANT_URL}$1"` 244 CODE=`echo "$RESP" | tail -n1` 245 case "$CODE" in 246 20*) 247 ;; 248 *) 249 echo " FAILED ($CODE)" 250 echo "$RESP" | head -n-1 251 exit_fail "merchant rejected POST /$1" 252 ;; 253 esac 254 } 255 # The default instance is called "admin" and is served at the root of the 256 # merchant; the historic /instances/default/ prefix is gone. 'use_stefan' 257 # is required by the current merchant protocol. 258 merchant_post management/instances \ 259 '{"auth":{"method":"external"},"id":"admin","name":"default","address":{},"jurisdiction":{},"use_stefan":true,"default_max_wire_fee":"TESTKUDOS:1","default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us":3600000000},"default_pay_delay":{"d_us":3600000000}}' 260 # 'fortythree' is one of the accounts taler-unified-setup.sh registers 261 merchant_post private/accounts \ 262 '{"payto_uri":"payto://x-taler-bank/localhost/fortythree?receiver-name=Forty+Three"}' 263 echo " OK" 264 265 echo -n "Preparing wallet" 266 rm -f $WALLET_DB 267 taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api \ 268 --expect-success 'withdrawTestBalance' \ 269 "$(jq -n ' 270 { 271 amount: "TESTKUDOS:100", 272 corebankApiBaseUrl: $BANK_URL, 273 exchangeBaseUrl: $EXCHANGE_URL 274 }' \ 275 --arg BANK_URL "$BANK_URL" \ 276 --arg EXCHANGE_URL "$EXCHANGE_URL" 277 )" 2> wallet-withdraw.log >/dev/null 278 echo -n "." 279 # the wallet's withdrawal only completes once the exchange has seen the 280 # incoming wire transfer 281 taler-exchange-wirewatch -c $CONF.edited -t 2> wirewatch-once.log 282 echo -n "." 283 taler-wallet-cli --wallet-db=$WALLET_DB run-until-done 2> wallet-run.log >/dev/null 284 echo " OK" 285 286 echo -n "Setting up IBAN authentication ..." 287 # The IBAN challenge needs a bank that speaks IBAN and whose incoming 288 # transfers Anastasis can see through the taler-revenue API. That cannot 289 # be the bank the exchange uses: libeufin-bank serves a single WIRE_TYPE, 290 # and the exchange side of this test is x-taler-bank. So we run a second, 291 # IBAN-typed libeufin-bank for this one authorization method -- it also 292 # matches the fact that the IBAN challenge is priced in EUR. 293 IBAN_DB=anastasisibancheck 294 IBAN_BANK_PORT=8092 295 IBAN_BANK_URL="http://localhost:${IBAN_BANK_PORT}/" 296 REASON="" 297 # both IBANs must pass the checksum: libeufin-bank rejects malformed ones 298 IBAN_CREDIT="DE44500105175407324931" 299 IBAN_DEBIT="FR1420041010050500013M02606" 300 PERSON_CREDIT_NAME="Person Credit" 301 CREDIT_USERNAME=anastasis-credit-user 302 CREDIT_PASSWORD=anastasis-credit-password 303 DEBIT_USERNAME=anastasis-debit-user 304 DEBIT_PASSWORD=anastasis-debit-password 305 PAYTO_CREDIT="payto://iban/${IBAN_CREDIT}?receiver-name=Person+Credit" 306 PAYTO_DEBIT="payto://iban/${IBAN_DEBIT}?receiver-name=Person+Debit" 307 IBAN_REVENUE_URL="${IBAN_BANK_URL}accounts/${CREDIT_USERNAME}/taler-revenue/" 308 # the challenge is priced in this currency, e.g. "EUR:1" 309 IBAN_CURRENCY=`anastasis-config -c $CONF_4 -s authorization-iban -o COST | cut -d: -f1` 310 cat > $CONF_IBAN <<EOF 311 # Generated by test_prepare.sh: the "customer's bank" for IBAN challenges. 312 [PATHS] 313 TALER_HOME = \${PWD}/test_reducer_home/ 314 TALER_DATA_HOME = \$TALER_HOME/.local/share/taler/ 315 TALER_CONFIG_HOME = \$TALER_HOME/.config/taler/ 316 TALER_CACHE_HOME = \$TALER_HOME/.cache/taler/ 317 TALER_RUNTIME_DIR = \${TMPDIR:-\${TMP:-/tmp}}/taler-system-runtime-\${USER}/ 318 319 [libeufin-bank] 320 CURRENCY = ${IBAN_CURRENCY} 321 BASE_URL = ${IBAN_BANK_URL} 322 SERVE = tcp 323 PORT = ${IBAN_BANK_PORT} 324 WIRE_TYPE = iban 325 IBAN_PAYTO_BIC = SANDBOXX 326 DEFAULT_CUSTOMER_DEBT_LIMIT = ${IBAN_CURRENCY}:200 327 DEFAULT_ADMIN_DEBT_LIMIT = ${IBAN_CURRENCY}:2000 328 ALLOW_REGISTRATION = YES 329 PWD_HASH_CONFIG = { "cost": 4 } 330 PWD_AUTH_COMPAT = yes 331 332 [libeufin-bankdb-postgres] 333 CONFIG = postgres:///${IBAN_DB} 334 EOF 335 IBAN_ACTIVE='true' 336 dropdb $IBAN_DB >/dev/null 2>/dev/null || true 337 if ! createdb $IBAN_DB 2>> iban-setup.log 338 then 339 IBAN_ACTIVE='false' 340 else 341 libeufin-bank dbinit -c $CONF_IBAN >> iban-setup.log 2>&1 342 libeufin-bank serve -c $CONF_IBAN >> iban-setup.log 2>&1 & 343 IBAN_BANK_PID=$! 344 for n in `seq 1 50` 345 do 346 sleep 0.2 347 wget --tries=1 --timeout=1 "${IBAN_BANK_URL}config" \ 348 -o /dev/null -O /dev/null >/dev/null && break 349 done 350 make_bank_account "$CREDIT_USERNAME" "$CREDIT_PASSWORD" \ 351 "$PERSON_CREDIT_NAME" "$PAYTO_CREDIT" > /dev/null && 352 make_bank_account "$DEBIT_USERNAME" "$DEBIT_PASSWORD" \ 353 "Person Debit" "$PAYTO_DEBIT" > /dev/null && 354 libeufin-bank edit-account -c $CONF_IBAN \ 355 --debit_threshold="${IBAN_CURRENCY}:1000" \ 356 "$DEBIT_USERNAME" >> iban-setup.log 2>&1 || 357 IBAN_ACTIVE='false' 358 # Make sure the bank speaks the revenue API that 359 # anastasis-helper-authorization-iban expects (an empty history is 360 # answered with 204), so that a version mismatch disables IBAN 361 # authentication instead of failing challenges at run time. 362 CODE=`curl -s -o /dev/null -w "%{http_code}" \ 363 --user "${CREDIT_USERNAME}:${CREDIT_PASSWORD}" \ 364 "${IBAN_REVENUE_URL}history?limit=1"` 365 if test $IBAN_ACTIVE = 'true' && 366 test 200 != "$CODE" && test 204 != "$CODE" 367 then 368 REASON="revenue API at ${IBAN_REVENUE_URL} answered $CODE" 369 IBAN_ACTIVE='false' 370 kill $IBAN_BANK_PID 2> /dev/null || true 371 fi 372 fi 373 if test $IBAN_ACTIVE = 'false' 374 then 375 echo " DISABLED (${REASON:-see iban-setup.log})" 376 anastasis-config -c $CONF_4 -s authorization-iban -o ENABLED -V NO 377 else 378 anastasis-config -c $CONF_4 -s authorization-iban -o CURRENCY -V "${IBAN_CURRENCY}" 379 anastasis-config -c $CONF_4 -s authorization-iban -o CREDIT_IBAN -V "${IBAN_CREDIT}" 380 anastasis-config -c $CONF_4 -s authorization-iban -o BUSINESS_NAME -V "${PERSON_CREDIT_NAME}" 381 anastasis-config -c $CONF_4 -s authorization-iban -o WIRE_GATEWAY_URL \ 382 -V "${IBAN_REVENUE_URL}" 383 anastasis-config -c $CONF_4 -s authorization-iban -o WIRE_GATEWAY_AUTH_METHOD -V "basic" 384 anastasis-config -c $CONF_4 -s authorization-iban -o USERNAME -V "${CREDIT_USERNAME}" 385 anastasis-config -c $CONF_4 -s authorization-iban -o PASSWORD -V "${CREDIT_PASSWORD}" 386 anastasis-config -c $CONF_4 -s authorization-iban -o ENABLED -V YES 387 echo " OK" 388 fi 389 else 390 anastasis-config -c $CONF_4 -s authorization-iban -o ENABLED -V NO 391 fi 392 393 394 echo -n "Launching anastasis services ..." 395 # PREFIX="valgrind --log-file=anastasis-httpd.%p.log" 396 PREFIX="" 397 $PREFIX anastasis-httpd -L INFO -c $CONF_1 2> anastasis-httpd_1.log & 398 PPID_1=$! 399 $PREFIX anastasis-httpd -L INFO -c $CONF_2 2> anastasis-httpd_2.log & 400 PPID_2=$! 401 $PREFIX anastasis-httpd -L INFO -c $CONF_3 2> anastasis-httpd_3.log & 402 PPID_3=$! 403 $PREFIX anastasis-httpd -L INFO -c $CONF_4 2> anastasis-httpd_4.log & 404 PPID_4=$! 405 export PPID_1 406 export PPID_2 407 export PPID_3 408 export PPID_4 409 echo " OK" 410 411 echo -n "Waiting for anastasis services ..." 412 413 # Wait for anastasis services to be available 414 for n in `seq 1 50` 415 do 416 echo -n "." 417 sleep 0.1 418 OK=0 419 # anastasis_01 420 wget --tries=1 --timeout=1 http://localhost:8086/ -o /dev/null -O /dev/null >/dev/null || continue 421 # anastasis_02 422 wget --tries=1 --timeout=1 http://localhost:8087/ -o /dev/null -O /dev/null >/dev/null || continue 423 # anastasis_03 424 wget --tries=1 --timeout=1 http://localhost:8088/ -o /dev/null -O /dev/null >/dev/null || continue 425 # anastasis_04 426 wget --tries=1 --timeout=1 http://localhost:8089/ -o /dev/null -O /dev/null >/dev/null || continue 427 OK=1 428 break 429 done 430 431 if [ 1 != $OK ] 432 then 433 exit_skip "Failed to launch anastasis services" 434 fi 435 echo "OK" 436 437 export -f wire_transfer_to_anastasis 438 439 echo "You can now run anastasis-gtk in TESTLAND." 440 echo " " 441 echo "We will now start a sub-shell. Please note:" 442 echo '- to terminate the test environment by leaving the sub-shell use: exit' 443 if test $IBAN_ACTIVE = 'true' 444 then 445 echo '- for IBAN authentication use: wire_transfer_to_anastasis "$AMOUNT" "$SUBJECT"' 446 echo "- for your customer IBAN, use: ${IBAN_DEBIT}" 447 export IBAN_BANK_URL 448 export PAYTO_CREDIT 449 export DEBIT_USERNAME 450 export DEBIT_PASSWORD 451 export CONF_4 452 fi 453 if test $1 = 'fees' 454 then 455 echo '- to pay, use: taler-wallet-cli --wallet-db=$WALLET_DB handle-uri $PAY_URI -y' 456 export WALLET_DB 457 fi 458 459 echo '- use kill -SIGSTOP $PPID_$NUM ($NUM: 1-4) to suspend providers' 460 461 bash 462 463 exit 0