sandcastle-ng

Scripts for the deployment of Sandcastle (GNU Taler)
Log | Files | Refs | README

setup-sandcastle.sh (41254B)


      1 #!/usr/bin/env bash
      2 
      3 # This scripts provisions all configuration and
      4 # services for the Taler sandcastle container.
      5 #
      6 # Important: This script needs to be completely
      7 # idempotent, nothing must break if it is executed
      8 # multiple times.
      9 
     10 set -eu
     11 set -x
     12 export LC_ALL="C.UTF-8"
     13 
     14 if [[ -n ${SANDCASTLE_SKIP_SETUP:-} ]]; then
     15   echo "skipping sandcastle setup, requested by environment var SANDCASTLE_SKIP_SETUP"
     16   exit 1
     17 fi
     18 
     19 
     20 # Helper to replace a comment-delimited block of lines in a config file with
     21 # the desired content. If the block doesn't exist yet, append it.
     22 update_config_block() {
     23   local config_file="$1"
     24   local marker_tag="$2"
     25   local new_content="$3"
     26   local begin_marker="# begin ${marker_tag}"
     27   local end_marker="# end ${marker_tag}"
     28   if [[ ! -f "$config_file" ]]; then
     29     echo "Error: Config file '$config_file' not found." >&2
     30     return 1
     31   fi
     32   if grep -qF "$begin_marker" "$config_file"; then
     33     # Markers exist. Replace the block.
     34     # Escape newlines in the content so sed processes it as a single block
     35     local escaped_content="${new_content//$'\n'/\\n}"
     36     sed -i "/$begin_marker/,/$end_marker/c\\$begin_marker\n$escaped_content\n$end_marker" "$config_file"
     37   else
     38     # Markers do not exist. Append to the end.
     39     printf "\n%s\n%s\n%s\n" "$begin_marker" "$new_content" "$end_marker" >> "$config_file"
     40   fi
     41 }
     42 
     43 echo "Provisioning sandcastle"
     44 
     45 # General configuration.
     46 # Might eventually be moved to an external file.
     47 
     48 # Source any overrides from external file
     49 if [[ -e /overrides ]]; then
     50   source /overrides
     51 fi
     52 
     53 # When serving on an external port (for localhost deployments),
     54 # we use http.
     55 if [[ ${EXTERNAL_PORT:-} =~ ^[0-9]+$ ]]; then
     56   PROTO=http
     57   PORT_SUFFIX=:$EXTERNAL_PORT
     58 else
     59   PROTO=https
     60   PORT_SUFFIX=
     61 fi
     62 
     63 : ${CURRENCY:="KUDOS"}
     64 : ${WIRE_METHOD:=x-taler-bank}
     65 
     66 
     67 if [[ $WIRE_METHOD = iban ]]; then
     68   EXCHANGE_IBAN=DE159593
     69   EXCHANGE_PAYTO="payto://iban/$EXCHANGE_IBAN?receiver-name=Sandcastle+Echange+Inc"
     70 
     71   # Randomly generated IBANs for the merchants
     72   MERCHANT_IBAN_ADMIN=DE85500105175178585583
     73   MERCHANT_IBAN_POS=DE4218710
     74   MERCHANT_IBAN_BLOG=DE8292195
     75   MERCHANT_IBAN_GNUNET=DE9709960
     76   MERCHANT_IBAN_TALER=DE1740597
     77   MERCHANT_IBAN_TOR=DE2648777
     78   MERCHANT_IBAN_SANDBOX=DE949115029592
     79   MERCHANT_IBAN_UMAMI=DE358841382499
     80   MERCHANT_IBAN_TESTING=DE38936850270207298566
     81 
     82   MERCHANT_PAYTO_ADMIN="payto://iban/$MERCHANT_IBAN_ADMIN?receiver-name=Default+Merchant"
     83   MERCHANT_PAYTO_TESTING="payto://iban/$MERCHANT_IBAN_TESTING?receiver-name=Testing+Merchant"
     84   MERCHANT_PAYTO_POS="payto://iban/$MERCHANT_IBAN_POS?receiver-name=PoS+Merchant"
     85   MERCHANT_PAYTO_BLOG="payto://iban/$MERCHANT_IBAN_BLOG?receiver-name=Blog+Merchant"
     86   MERCHANT_PAYTO_GNUNET="payto://iban/$MERCHANT_IBAN_GNUNET?receiver-name=GNUnet+Merchant"
     87   MERCHANT_PAYTO_TALER="payto://iban/$MERCHANT_IBAN_TALER?receiver-name=Taler+Merchant"
     88   MERCHANT_PAYTO_TOR="payto://iban/$MERCHANT_IBAN_TOR?receiver-name=Tor+Merchant"
     89   MERCHANT_PAYTO_UMAMI="payto://iban/$MERCHANT_IBAN_UMAMI?receiver-name=Umami"
     90   MERCHANT_PAYTO_SANDBOX="payto://iban/$MERCHANT_IBAN_SANDBOX?receiver-name=Sandbox+Merchant"
     91 elif [[ $WIRE_METHOD = x-taler-bank ]]; then
     92   XTBHOST=sandcastle
     93   EXCHANGE_PAYTO="payto://x-taler-bank/$XTBHOST/exchange?receiver-name=Sandcastle+Exchange+Inc"
     94   MERCHANT_PAYTO_ADMIN="payto://x-taler-bank/$XTBHOST/merchant-admin?receiver-name=Admin+Merchant"
     95   MERCHANT_PAYTO_TESTING="payto://x-taler-bank/$XTBHOST/merchant-testing?receiver-name=Default+Merchant"
     96   MERCHANT_PAYTO_POS="payto://x-taler-bank/$XTBHOST/merchant-pos?receiver-name=PoS+Merchant"
     97   MERCHANT_PAYTO_BLOG="payto://x-taler-bank/$XTBHOST/merchant-blog?receiver-name=Blog+Merchant"
     98   MERCHANT_PAYTO_GNUNET="payto://x-taler-bank/$XTBHOST/merchant-gnunet?receiver-name=GNUnet+Merchant"
     99   MERCHANT_PAYTO_TALER="payto://x-taler-bank/$XTBHOST/merchant-taler?receiver-name=Taler+Merchant"
    100   MERCHANT_PAYTO_TOR="payto://x-taler-bank/$XTBHOST/merchant-tor?receiver-name=Tor+Merchant"
    101   MERCHANT_PAYTO_UMAMI="payto://x-taler-bank/$XTBHOST/merchant-umami?receiver-name=Umami"
    102   MERCHANT_PAYTO_SANDBOX="payto://x-taler-bank/$XTBHOST/merchant-sandbox?receiver-name=Sandbox+Merchant"
    103 else
    104   echo "wire method $WIRE_METHOD not supported"
    105   exit 1
    106 fi
    107 
    108 MYDOMAIN=${MYDOMAIN:="demo.taler.net"}
    109 LANDING_DOMAIN=$MYDOMAIN
    110 BANK_DOMAIN=bank.$MYDOMAIN
    111 EXCHANGE_DOMAIN=exchange.$MYDOMAIN
    112 MERCHANT_DOMAIN=backend.$MYDOMAIN
    113 DONAU_DOMAIN=donau.$MYDOMAIN
    114 BLOG_DOMAIN=shop.$MYDOMAIN
    115 DONATIONS_DOMAIN=donations.$MYDOMAIN
    116 DRUPAL_DOMAIN=drupal.$MYDOMAIN
    117 CHALLENGER_DOMAIN=challenger.$MYDOMAIN
    118 AUDITOR_DOMAIN=auditor.$MYDOMAIN
    119 
    120 # Ports of the services running inside the container.
    121 # Should be synchronized with the sandcastle-run script.
    122 PORT_INTERNAL_EXCHANGE=8201
    123 PORT_INTERNAL_MERCHANT=8301
    124 PORT_INTERNAL_LIBEUFIN_BANK=8080
    125 PORT_INTERNAL_LANDING=8501
    126 PORT_INTERNAL_BLOG=8502
    127 PORT_INTERNAL_DONATIONS=8503
    128 PORT_INTERNAL_PROVISION=8504
    129 PORT_INTERNAL_BANK_SPA=8505
    130 PORT_INTERNAL_CHALLENGER=8506
    131 PORT_INTERNAL_AUDITOR=8507
    132 PORT_INTERNAL_DONAU=8508
    133 PORT_INTERNAL_DRUPAL=8509
    134 
    135 
    136 ENABLE_AUDITOR=0
    137 
    138 # Just make sure the services are stopped
    139 systemctl stop postgresql.service
    140 systemctl stop taler-auditor.target
    141 systemctl stop taler-exchange.target
    142 systemctl stop taler-exchange-offline.timer
    143 systemctl stop taler-merchant-httpd.service
    144 systemctl stop taler-merchant.target
    145 systemctl stop taler-demo-landing.service
    146 systemctl stop taler-demo-blog.service
    147 systemctl stop taler-demo-donations.service
    148 systemctl stop libeufin-bank.service
    149 systemctl stop donau-httpd.service
    150 
    151 # libeufin-nexus is not used
    152 systemctl stop libeufin-nexus-ebics-fetch.service
    153 systemctl disable libeufin-nexus-ebics-fetch.service
    154 systemctl stop libeufin-nexus-ebics-submit.service
    155 systemctl disable libeufin-nexus-ebics-submit.service
    156 
    157 systemctl reset-failed
    158 
    159 # We now make sure that some important locations are symlinked to
    160 # the persistent storage volume.
    161 # Files that already exist in this location are moved to the storage volume
    162 # and then symlinked.
    163 # These locations are:
    164 # /etc/taler
    165 # /etc/libeufin
    166 # /var/lib/taler
    167 # postgres DB directory
    168 
    169 function lift_dir() {
    170   where=$1
    171   src=$2
    172   target=$3
    173   if [[ -L $src ]]; then
    174     # be idempotent
    175     echo "$src is already a symlink"
    176   elif [[ -d /$where/$target ]]; then
    177     echo "symlinking existing /$where/$target"
    178     rm -rf "$src"
    179     ln -s "/$where/$target" "$src"
    180   else
    181     echo "symlinking new /$where/$target"
    182     mv "$src" "/$where/$target"
    183     ln -s "/$where/$target" "$src"
    184   fi
    185 }
    186 
    187 lift_dir talerdata /var/lib/taler-exchange var-lib-taler-exchange
    188 lift_dir talerdata /etc/taler-merchant etc-taler-merchant
    189 lift_dir talerdata /etc/taler-exchange etc-taler-exchange
    190 lift_dir talerdata /etc/taler-exchange etc-taler-auditor
    191 lift_dir talerdata /etc/donau etc-donau
    192 lift_dir talerdata /etc/libeufin etc-libeufin
    193 # lift both config and data
    194 lift_dir talerdata /etc/postgresql etc-postgresql
    195 lift_dir talerdata /var/lib/postgresql var-lib-postgresql
    196 # offline keys are in a separate volume
    197 lift_dir talerdata_persistent /var/lib/taler-exchange/offline exchange-offline
    198 
    199 # Usage: get_credential_pw COMPONENT/ACCOUNT
    200 function get_credential_pw() {
    201   if [[ ${USE_INSECURE_SANDBOX_PASSWORDS:-0} = 1 ]]; then
    202     echo "sandbox"
    203     return
    204   fi
    205   p=/credentials/$1
    206   if [[ ! -f $p ]]; then
    207     mkdir -p $(dirname "$p")
    208     uuidgen -r >$p
    209   fi
    210   cat "$p"
    211 }
    212 
    213 import_instr=none
    214 if [[ -d /exported && -e /exported/import-request ]]; then
    215   import_instr=$(cat /exported/import-request)
    216 fi
    217 
    218 # If necessary, import the offline key.
    219 # Done before everything else, as we need the key
    220 # to generate the config.
    221 
    222 if [[ $import_instr = all ]]; then
    223   echo "Importing exchange offline key"
    224   rm -rf /var/lib/taler-exchange/offline/*
    225   cp -r /exported/taler-exchange/offline/* /var/lib/taler-exchange/offline/
    226 fi
    227 
    228 # Adjust ownership.
    229 # Necessary when the container is rebuilt with different user IDs.
    230 chown --recursive taler-exchange-offline:taler-exchange-offline /var/lib/taler-exchange/offline/. || true
    231 chown root:taler-exchange-db /etc/taler-exchange/secrets/exchange-db.secret.conf
    232 chown taler-exchange-wire:root /etc/taler-exchange/secrets/exchange-accountcredentials-*.conf
    233 chown taler-merchant-httpd:root /etc/taler-merchant/secrets/merchant-db.secret.conf
    234 chown root:donau-db /etc/donau/secrets/donau-db.secret.conf
    235 
    236 
    237 MASTER_PUBLIC_KEY=$(sudo -u taler-exchange-offline taler-exchange-offline -LDEBUG setup)
    238 
    239 
    240 #
    241 # Create the basic configuration files
    242 #
    243 
    244 mkdir -p /etc/challenger/conf.d
    245 cat <<EOF >/etc/challenger/conf.d/setup-sandcastle.conf
    246 [challenger]
    247 BASE_URL = $PROTO://$CHALLENGER_DOMAIN$PORT_SUFFIX/
    248 ADDRESS_TYPE = email
    249 AUTH_COMMAND = /data/sandcastle-challenger-auth
    250 ADDRESS_RESTRICTIONS = {"email":{"hint":"not an e-mail address","regex":"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$"}}
    251 EOF
    252 
    253 cat <<EOF >/etc/libeufin/libeufin-bank.conf
    254 [libeufin-bank]
    255 BASE_URL = $PROTO://$BANK_DOMAIN$PORT_SUFFIX/
    256 CURRENCY = $CURRENCY
    257 DEFAULT_DEBT_LIMIT = $CURRENCY:500
    258 REGISTRATION_BONUS = $CURRENCY:100
    259 SPA_CAPTCHA_URL = $PROTO://$BANK_DOMAIN$PORT_SUFFIX/webui/#/operation/{woid}
    260 SUGGESTED_WITHDRAWAL_EXCHANGE = $PROTO://$EXCHANGE_DOMAIN$PORT_SUFFIX/
    261 ALLOW_REGISTRATION = yes
    262 SERVE = tcp
    263 PORT = 8080
    264 # Bind address.
    265 # Option soon to be deprecated!
    266 ADDRESS = 0.0.0.0
    267 WIRE_TYPE = $WIRE_METHOD
    268 
    269 # Compat mode for now
    270 PWD_CHECK = no
    271 PWD_AUTH_COMPAT = yes
    272 
    273 [currency-$CURRENCY]
    274 ENABLED = YES
    275 name = "${NAME:=Kudos}"
    276 code = "$CURRENCY"
    277 decimal_separator = "."
    278 fractional_input_digits = ${FRACTIONALS:=2}
    279 fractional_normal_digits = ${FRACTIONALS:=2}
    280 fractional_trailing_zero_digits = ${FRACTIONALS:=2}
    281 is_currency_name_leading = NO
    282 alt_unit_names = {"0":"${ALT_UNIT_NAME:=ク}"}
    283 EOF
    284 
    285 cat <<EOF >/etc/libeufin/settings.json
    286 {
    287   "topNavSites": {
    288     "Landing": "$PROTO://$LANDING_DOMAIN$PORT_SUFFIX/",
    289     "Bank": "$PROTO://$BANK_DOMAIN$PORT_SUFFIX",
    290     "Essay Shop": "$PROTO://$BLOG_DOMAIN$PORT_SUFFIX",
    291     "Donations": "$PROTO://$DONATIONS_DOMAIN$PORT_SUFFIX",
    292   }
    293 }
    294 EOF
    295 
    296 # Generate /tmp/sandcastle-setup.conf
    297 cat <<EOF >/tmp/sandcastle-setup.conf
    298 [currency-$CURRENCY]
    299 ENABLED = YES
    300 name = "${NAME:=Kudos}"
    301 code = "$CURRENCY"
    302 decimal_separator = "."
    303 fractional_input_digits = ${FRACTIONALS:=2}
    304 fractional_normal_digits = ${FRACTIONALS:=2}
    305 fractional_trailing_zero_digits = ${FRACTIONALS:=2}
    306 is_currency_name_leading = NO
    307 alt_unit_names = {"0":"${ALT_UNIT_NAME:=ク}"}
    308 EOF
    309 
    310 cp /tmp/sandcastle-setup.conf /etc/taler-exchange/conf.d/sandcastle-setup.conf
    311 cp /tmp/sandcastle-setup.conf /etc/taler-merchant/conf.d/sandcastle-setup.conf
    312 
    313 
    314 cat <<EOF >/etc/taler-exchange/conf.d/sandcastle-exchange.conf
    315 [exchange]
    316 CURRENCY = $CURRENCY
    317 CURRENCY_ROUND_UNIT = $CURRENCY:0.01
    318 TINY_AMOUNT = $CURRENCY:0.01
    319 AML_THRESHOLD = $CURRENCY:1000000
    320 MASTER_PUBLIC_KEY = $MASTER_PUBLIC_KEY
    321 BASE_URL = $PROTO://$EXCHANGE_DOMAIN$PORT_SUFFIX/
    322 
    323 [taler-exchange-secmod-rsa]
    324 LOOKAHEAD_SIGN = 4 weeks
    325 
    326 [taler-exchange-secmod-eddsa]
    327 LOOKAHEAD_SIGN = 4 weeks
    328 
    329 [taler-exchange-secmod-cs]
    330 LOOKAHEAD_SIGN = 4 weeks
    331 
    332 [exchange-account-default]
    333 PAYTO_URI = $EXCHANGE_PAYTO
    334 ENABLE_DEBIT = YES
    335 ENABLE_CREDIT = YES
    336 @inline-secret@ exchange-accountcredentials-default ../secrets/exchange-accountcredentials-default.secret.conf
    337 EOF
    338 
    339 
    340 cat <<EOF >/etc/taler-exchange/secrets/exchange-accountcredentials-default.secret.conf
    341 [exchange-accountcredentials-default]
    342 WIRE_GATEWAY_URL = $PROTO://$BANK_DOMAIN$PORT_SUFFIX/accounts/exchange/taler-wire-gateway/
    343 WIRE_GATEWAY_AUTH_METHOD = basic
    344 USERNAME = exchange
    345 PASSWORD = $(get_credential_pw bank/exchange)
    346 EOF
    347 
    348 if [[ $ENABLE_AUDITOR = 1 ]]; then
    349   # Make sandcastle exchange config available to auditor
    350   cp /etc/taler-exchange/conf.d/sandcastle-exchange.conf /etc/taler-auditor/conf.d/sandcastle-exchange.conf
    351 
    352   # We run the offline tooling as root, maybe in the future there should be
    353   # a separate user created by the Debian package for that.
    354   AUDITOR_PUB=$(taler-auditor-offline setup)
    355 
    356   cat <<EOF >/etc/taler-auditor/conf.d/sandcastle-auditor.conf
    357 [auditor]
    358 PUBLIC_KEY = $AUDITOR_PUB
    359 
    360 [exchangedb]
    361 
    362 $(dup_exchange_opt exchangedb IDLE_RESERVE_EXPIRATION_TIME)
    363 $(dup_exchange_opt exchangedb LEGAL_RESERVE_EXPIRATION_TIME)
    364 $(dup_exchange_opt exchangedb AGGREGATOR_SHIFT)
    365 $(dup_exchange_opt exchangedb DEFAULT_PURSE_LIMIT)
    366 
    367 [exchangedb-postgres]
    368 $(dup_exchange_opt exchangedb-postgres CONFIG)
    369 
    370 [exchange]
    371 $(dup_exchange_opt exchange CURRENCY)
    372 $(dup_exchange_opt exchange CURRENCY_ROUND_UNIT)
    373 $(dup_exchange_opt exchange DB)
    374 
    375 
    376 EOF
    377 fi
    378 
    379 # The config shipped with the package can conflict with the
    380 # trusted sandcastle exchange if the currency is KUDOS.
    381 rm -f /usr/share/taler-exchange/config.d/kudos.conf
    382 rm -f /usr/share/taler-merchant/config.d/kudos.conf
    383 
    384 MY_HELPER_EMAIL=${OVERRIDE_MERCHANT_HELPER_EMAIL:-/data/sandcastle-merchant-email-helper}
    385 
    386 # We need to define the default currency for the UI.
    387 cat <<EOF >/etc/taler-merchant/conf.d/sandcastle-merchant.conf
    388 [merchant]
    389 # Default currency
    390 CURRENCY = $CURRENCY
    391 ENABLE_SELF_PROVISIONING = YES
    392 MANDATORY_TAN_CHANNELS = email
    393 HELPER_EMAIL = $MY_HELPER_EMAIL
    394 
    395 BASE_URL = $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/
    396 
    397 # Will be the default in future merchant versions
    398 # => Can be removed after 1.6 release
    399 BACKOFFICE_SPA_DIR = /usr/share/taler-merchant-webui/
    400 
    401 EOF
    402 
    403 cat <<EOF >/etc/taler-merchant/conf.d/sandcastle-merchant-exchanges.conf
    404 [merchant-exchange-sandcastle]
    405 EXCHANGE_BASE_URL = $PROTO://$EXCHANGE_DOMAIN$PORT_SUFFIX/
    406 MASTER_KEY = $MASTER_PUBLIC_KEY
    407 CURRENCY = $CURRENCY
    408 EOF
    409 
    410 # Allow overrides to modify merchant config
    411 [[ $(type -t hook_merchant_config) == function ]] && hook_merchant_config
    412 
    413 # FIXME: This is a workaround, fix the packaging of taler-merchant-frontends here!
    414 mkdir -p /etc/taler
    415 
    416 cat <<EOF >/etc/taler/taler-merchant-frontends.conf
    417 # Different entry point, we need to repeat some settings.
    418 # In the future, taler-merchant-demos should become
    419 # robust enough to read from the main config.
    420 [taler]
    421 CURRENCY = $CURRENCY
    422 
    423 [frontend-demo-landing]
    424 SERVE = http
    425 HTTP_PORT = $PORT_INTERNAL_LANDING
    426 
    427 [frontend-demo-blog]
    428 SERVE = http
    429 HTTP_PORT = $PORT_INTERNAL_BLOG
    430 BACKEND_URL = $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/blog/
    431 BACKEND_APIKEY = secret-token:$(get_credential_pw merchant/blog)
    432 ENABLE_TOKENS = ${ENABLE_SUBSCRIPTIONS:-no}
    433 
    434 [frontend-demo-donations]
    435 DONAU_URL = $PROTO://$DONAU_DOMAIN$PORT_SUFFIX/
    436 SERVE = http
    437 HTTP_PORT = $PORT_INTERNAL_DONATIONS
    438 BACKEND_URL_TOR = $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/tor/
    439 BACKEND_APIKEY_TOR = secret-token:$(get_credential_pw merchant/tor)
    440 BACKEND_URL_TALER = $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/taler/
    441 BACKEND_APIKEY_TALER = secret-token:$(get_credential_pw merchant/taler)
    442 BACKEND_URL_GNUNET = $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/gnunet/
    443 BACKEND_APIKEY_GNUNET = secret-token:$(get_credential_pw merchant/gnunet)
    444 EOF
    445 
    446 # This really should not exist, the taler-merchant-frontends
    447 # should be easier to configure!
    448 cat <<EOF >/etc/taler/taler-merchant-frontends.env
    449 TALER_ENV_URL_INTRO=$PROTO://$LANDING_DOMAIN$PORT_SUFFIX/
    450 TALER_ENV_URL_LANDING=$PROTO://$LANDING_DOMAIN$PORT_SUFFIX/
    451 TALER_ENV_URL_BANK=$PROTO://$BANK_DOMAIN$PORT_SUFFIX/
    452 TALER_ENV_URL_MERCHANT_BLOG=$PROTO://$BLOG_DOMAIN$PORT_SUFFIX/
    453 TALER_ENV_URL_MERCHANT_DONATIONS=$PROTO://$DONATIONS_DOMAIN$PORT_SUFFIX/
    454 EOF
    455 
    456 #
    457 # Create databases
    458 #
    459 
    460 function wait_pg_ready() {
    461   while true; do
    462     ret=0
    463     pg_isready || ret=$?
    464     case "$ret" in
    465       0)
    466         echo "Postgres is ready" >&2
    467         break
    468         ;;
    469       1|2)
    470         echo "pg_isready returned status $ret, waiting" >&2
    471         sleep 1
    472         ;;
    473       3)
    474         echo "pg_isready returned status $ret, giving up" >&2
    475         exit 3
    476         ;;
    477     esac
    478   done
    479 }
    480 
    481 PG_VERSION="17"
    482 
    483 # Since the sandcastle is a test system, we turn fsync off for performance
    484 # reasons (especially with the drupal setup).
    485 # CAUTION: You do not want to set this in production,
    486 # especially not for the taler-exchange.
    487 pg_conftool $PG_VERSION main set fsync off
    488 # Higher limits needed for migration
    489 pg_conftool $PG_VERSION main set max_locks_per_transaction 512
    490 pg_conftool $PG_VERSION main set max_pred_locks_per_transaction 512
    491 
    492 backup_file=/exported/postgres-backup.sql
    493 if [[ $import_instr = singledump ]]; then
    494   echo "Importing database dump"
    495   if [[ ! -e "$backup_file" ]]; then
    496     echo "Requested import, but backup file does not exist" >&2
    497     exit 1
    498   fi
    499   pg_dropcluster --stop 17 main || true
    500   pg_createcluster 17 main
    501   systemctl start postgresql.service
    502   wait_pg_ready
    503   sudo -u postgres psql postgres -f "$backup_file"
    504 else
    505   systemctl start postgresql.service
    506   wait_pg_ready
    507 fi
    508 
    509 # Set up databases.
    510 # Do that *before* we potentially do a per-service restore-from-backup.
    511 
    512 challenger-dbconfig
    513 
    514 # Sets up the database for both libeufin-bank and libeufin-nexus.  We only need
    515 # the libeufin-bank DB though.
    516 libeufin-dbconfig
    517 
    518 if [[ $ENABLE_AUDITOR = 1 ]]; then
    519   # Add auditor user to DB group *before* running taler-exchange-dbconfig,
    520   # so that DB permissions are adjusted accordingly.
    521   usermod taler-auditor-httpd -aG taler-exchange-db
    522   taler-auditor-dbconfig
    523 fi
    524 
    525 taler-exchange-dbconfig
    526 
    527 taler-merchant-dbconfig
    528 
    529 
    530 #
    531 # Import backup if necessary.
    532 #
    533 
    534 if [[ $import_instr = all ]]; then
    535   echo "Importing databases"
    536 
    537   # FIXME: Consider backing up old DB before importing new one
    538   # FIXME: This is rather hacky, it would be better to use "pg_dump -Fc" and "pg_restore"
    539   sudo -u postgres dropdb taler-exchange
    540   sudo -u postgres dropdb taler-merchant
    541   sudo -u postgres dropdb libeufin
    542 
    543   sudo -u postgres createdb taler-exchange
    544   sudo -u postgres createdb taler-merchant
    545   sudo -u postgres createdb libeufin
    546 
    547   sudo -u postgres psql taler-exchange -f /exported/taler-exchange/taler-exchange.sql
    548   sudo -u postgres psql taler-merchant -f /exported/taler-merchant/taler-merchant.sql
    549   sudo -u postgres psql libeufin -f /exported/libeufin/libeufin.sql
    550 
    551   libeufin-dbconfig
    552   taler-exchange-dbconfig
    553   taler-merchant-dbconfig
    554 
    555   rm -rf /var/lib/taler-exchange/secmod-eddsa/*
    556   cp -r /exported/taler-exchange/secmod-eddsa/* /var/lib/taler-exchange/secmod-eddsa/
    557 
    558   rm -rf /var/lib/taler-exchange/secmod-rsa/*
    559   cp -r /exported/taler-exchange/secmod-rsa/* /var/lib/taler-exchange/secmod-rsa/
    560 
    561   rm -rf /var/lib/taler-exchange/secmod-cs/*
    562   cp -r /exported/taler-exchange/secmod-cs/* /var/lib/taler-exchange/secmod-cs/
    563 fi
    564 
    565 if [[ $import_instr != none ]]; then
    566   echo "Marking import as done"
    567   rm /exported/import-request
    568 fi
    569 
    570 # We need to adjust file ownership, as the container might have different user and group
    571 # IDs than the volume. That can happen when the packages in the container are installed
    572 # in a different order.
    573 # This is only relevant for non-root ownership.
    574 chown --recursive taler-exchange-offline:taler-exchange-offline /var/lib/taler-exchange/offline/* || true
    575 chown --recursive taler-exchange-secmod-cs:taler-exchange-secmod /var/lib/taler-exchange/secmod-cs
    576 chown --recursive taler-exchange-secmod-rsa:taler-exchange-secmod /var/lib/taler-exchange/secmod-rsa
    577 chown --recursive taler-exchange-secmod-eddsa:taler-exchange-secmod /var/lib/taler-exchange/secmod-eddsa
    578 chown root:taler-exchange-db /etc/taler-exchange/secrets/exchange-db.secret.conf
    579 chown root:taler-auditor-httpd /etc/taler-auditor/secrets/auditor-db.secret.conf
    580 chmod 440 /etc/taler-merchant/secrets/merchant-db.secret.conf
    581 chown taler-merchant-httpd:root /etc/taler-merchant/secrets/merchant-db.secret.conf
    582 chown root:taler-exchange-db /etc/taler-exchange/secrets/exchange-db.secret.conf
    583 chown taler-exchange-wire:taler-exchange-db /etc/taler-exchange/secrets/exchange-accountcredentials-default.secret.conf
    584 
    585 
    586 # Caddy configuration.
    587 # We use the caddy reverse proxy with automatic
    588 # internal TLS setup to ensure that the services are
    589 # reachable inside the container without any external
    590 # DNS setup under the same domain name and with TLS
    591 # from inside the container.
    592 
    593 systemctl stop caddy.service
    594 
    595 cat <<EOF >/etc/caddy/Caddyfile
    596 {
    597   servers {
    598       trusted_proxies static private_ranges
    599   }
    600 }
    601 
    602 # Services that only listen on unix domain sockets
    603 # are reverse-proxied to serve on a TCP port.
    604 
    605 :$PORT_INTERNAL_EXCHANGE {
    606   reverse_proxy unix//run/taler-exchange/httpd/exchange-http.sock
    607 }
    608 
    609 :$PORT_INTERNAL_MERCHANT {
    610   reverse_proxy unix//run/taler-merchant/httpd/merchant-http.sock {
    611     # Set this, or otherwise wrong taler://pay URIs will be generated.
    612     header_up X-Forwarded-Proto "https"
    613   }
    614 }
    615 
    616 :$PORT_INTERNAL_DONAU {
    617   reverse_proxy unix//run/donau/httpd/http.sock {
    618     header_up X-Forwarded-Proto "https"
    619   }
    620 }
    621 
    622 :$PORT_INTERNAL_BANK_SPA {
    623   root * /usr/share/libeufin/spa
    624   root /settings.json /etc/libeufin/
    625   file_server
    626 }
    627 
    628 :$PORT_INTERNAL_DRUPAL {
    629   root * /talerdata/sandcastle-drupal/web/
    630   php_fastcgi unix/var/run/php/php8.4-fpm.sock
    631   file_server
    632 }
    633 
    634 :$PORT_INTERNAL_AUDITOR {
    635   reverse_proxy unix//run/taler-auditor/httpd/auditor-http.sock
    636 }
    637 
    638 :$PORT_INTERNAL_CHALLENGER {
    639   handle {
    640     reverse_proxy unix//run/challenger/httpd/challenger.http {
    641       # Set this, or otherwise wrong taler://pay URIs will be generated.
    642       header_up X-Forwarded-Proto "https"
    643     }
    644   }
    645 
    646   # Serve challenges via HTTP.
    647   # This is obviously completely insecure, but fine
    648   # for the demo sandcastle.
    649   handle_path /challenges/* {
    650     root * /tmp/challenges/
    651     file_server {
    652       browse
    653     }
    654   }
    655 }
    656 EOF
    657 
    658 if [[ $PROTO = https ]]; then
    659   cat <<EOF >>/etc/caddy/Caddyfile
    660 
    661 # Internally reverse-proxy https://,
    662 # so that service can talk to each other via
    663 # https:// inside the container.
    664 
    665 https://$BANK_DOMAIN {
    666   tls internal
    667   reverse_proxy :8080 {
    668     # libeufin-bank should eventually not require this anymore,
    669     # but currently doesn't work without this header.
    670     header_up X-Forwarded-Prefix ""
    671   }
    672 }
    673 
    674 https://$EXCHANGE_DOMAIN {
    675   tls internal
    676   reverse_proxy unix//run/taler-exchange/httpd/exchange-http.sock
    677 }
    678 
    679 https://$MERCHANT_DOMAIN {
    680   tls internal
    681   reverse_proxy unix//run/taler-merchant/httpd/merchant-http.sock {
    682     # Set this, or otherwise wrong taler://pay URIs will be generated.
    683     header_up X-Forwarded-Proto "https"
    684   }
    685 }
    686 
    687 https://$DONAU_DOMAIN {
    688   tls internal
    689   reverse_proxy unix//run/donau/httpd/http.sock {
    690     header_up X-Forwarded-Proto "https"
    691   }
    692 }
    693 
    694 https://$AUDITOR_DOMAIN {
    695   tls internal
    696   reverse_proxy unix//run/taler-auditor/httpd/auditor-http.sock
    697 }
    698 
    699 https://$CHALLENGER_DOMAIN {
    700   tls internal
    701   reverse_proxy unix//run/challenger/httpd/challenger.http
    702 }
    703 
    704 EOF
    705 
    706 else
    707   # Config for HTTP without TLS.
    708 
    709   cat <<EOF >>/etc/caddy/Caddyfile
    710 
    711 http://$BANK_DOMAIN$PORT_SUFFIX {
    712   reverse_proxy :8080 {
    713     # libeufin-bank should eventually not require this anymore,
    714     # but currently doesn't work without this header.
    715     header_up X-Forwarded-Prefix ""
    716   }
    717 }
    718 
    719 http://$EXCHANGE_DOMAIN$PORT_SUFFIX {
    720   reverse_proxy unix//run/taler-exchange/httpd/exchange-http.sock
    721 }
    722 
    723 http://$MERCHANT_DOMAIN$PORT_SUFFIX {
    724   reverse_proxy unix//run/taler-merchant/httpd/merchant-http.sock
    725 }
    726 
    727 http://$DONAU_DOMAIN$PORT_SUFFIX {
    728   reverse_proxy unix//run/donau/httpd/http.sock
    729 }
    730 
    731 http://$AUDITOR_DOMAIN$PORT_SUFFIX {
    732   reverse_proxy unix//run/taler-auditor/httpd/auditor-http.sock
    733 }
    734 
    735 http://$CHALLENGER_DOMAIN$PORT_SUFFIX {
    736   reverse_proxy unix//run/challenger/httpd/challenger.http
    737 }
    738 
    739 http://$LANDING_DOMAIN$PORT_SUFFIX {
    740   reverse_proxy :$PORT_INTERNAL_LANDING
    741 }
    742 
    743 http://$BLOG_DOMAIN$PORT_SUFFIX {
    744   reverse_proxy :$PORT_INTERNAL_BLOG
    745 }
    746 
    747 http://$DONATIONS_DOMAIN$PORT_SUFFIX {
    748   reverse_proxy :$PORT_INTERNAL_DONATIONS
    749 }
    750 
    751 http://$DRUPAL_DOMAIN$PORT_SUFFIX {
    752   reverse_proxy :$PORT_INTERNAL_DRUPAL
    753 }
    754 
    755 EOF
    756 
    757 fi
    758 
    759 cat <<EOF >>/etc/hosts
    760 # Start of Taler Sandcastle Domains
    761 127.0.0.1 $LANDING_DOMAIN
    762 127.0.0.1 $BANK_DOMAIN
    763 127.0.0.1 $EXCHANGE_DOMAIN
    764 127.0.0.1 $MERCHANT_DOMAIN
    765 127.0.0.1 $BLOG_DOMAIN
    766 127.0.0.1 $DONATIONS_DOMAIN
    767 127.0.0.1 $CHALLENGER_DOMAIN
    768 127.0.0.1 $DRUPAL_DOMAIN
    769 # End of Taler Sandcastle Domains
    770 EOF
    771 
    772 systemctl start caddy.service
    773 
    774 # Install local, internal CA certs for caddy
    775 caddy trust
    776 
    777 # Set up challenger
    778 
    779 CHALLENGER_CLIENT_SECRET=secret-token:sandbox
    780 CHALLENGER_CLIENT_ID=$(sudo -u challenger-httpd challenger-admin -q --add="$CHALLENGER_CLIENT_SECRET" https://$EXCHANGE_DOMAIN/kyc-proof/mychallenger)
    781 echo Challenger client ID: $CHALLENGER_CLIENT_ID
    782 
    783 systemctl enable --now challenger-httpd.service
    784 
    785 # Set up bank
    786 
    787 sudo -u libeufin-bank libeufin-bank edit-account admin --debit_threshold=$CURRENCY:10000000
    788 sudo -u libeufin-bank libeufin-bank passwd admin $(get_credential_pw bank/admin)
    789 
    790 systemctl enable --now libeufin-bank.service
    791 
    792 BANK_BASEURL=$PROTO://$BANK_DOMAIN$PORT_SUFFIX/
    793 
    794 taler-harness deployment wait-taler-service taler-corebank ${BANK_BASEURL}config
    795 
    796 sudo -u libeufin-bank libeufin-bank passwd exchange $(get_credential_pw bank/exchange) || true
    797 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    798   --login exchange --exchange --public \
    799   --payto $EXCHANGE_PAYTO \
    800   --name Exchange \
    801   --password $(get_credential_pw bank/exchange)
    802 
    803 sudo -u libeufin-bank libeufin-bank passwd merchant-admin $(get_credential_pw bank/merchant-admin) || true
    804 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    805   --login merchant-admin --public \
    806   --payto $MERCHANT_PAYTO_ADMIN \
    807   --name "Default Demo Merchant" \
    808   --password $(get_credential_pw bank/merchant-admin)
    809 
    810 sudo -u libeufin-bank libeufin-bank passwd merchant-admin $(get_credential_pw bank/merchant-testing) || true
    811 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    812   --login merchant-testing --public \
    813   --payto $MERCHANT_PAYTO_TESTING \
    814   --name "Testing Merchant" \
    815   --password $(get_credential_pw bank/merchant-testing)
    816 
    817 sudo -u libeufin-bank libeufin-bank passwd merchant-pos $(get_credential_pw bank/merchant-pos) || true
    818 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    819   --login merchant-pos --public \
    820   --payto $MERCHANT_PAYTO_POS \
    821   --name "PoS Merchant" \
    822   --password $(get_credential_pw bank/merchant-pos)
    823 
    824 sudo -u libeufin-bank libeufin-bank passwd merchant-blog $(get_credential_pw bank/merchant-blog) || true
    825 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    826   --login merchant-blog --public \
    827   --payto $MERCHANT_PAYTO_BLOG \
    828   --name "Blog Merchant" \
    829   --password $(get_credential_pw bank/merchant-blog)
    830 
    831 sudo -u libeufin-bank libeufin-bank passwd merchant-gnunet $(get_credential_pw bank/merchant-gnunet) || true
    832 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    833   --login merchant-gnunet --public \
    834   --payto "$MERCHANT_PAYTO_GNUNET" \
    835   --name "GNUnet Donations Merchant" \
    836   --password $(get_credential_pw bank/merchant-gnunet)
    837 
    838 sudo -u libeufin-bank libeufin-bank passwd merchant-taler $(get_credential_pw bank/merchant-taler) || true
    839 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    840   --login merchant-taler --public \
    841   --payto "$MERCHANT_PAYTO_TALER" \
    842   --name "Taler Donations Merchant" \
    843   --password $(get_credential_pw bank/merchant-taler)
    844 
    845 sudo -u libeufin-bank libeufin-bank passwd merchant-tor $(get_credential_pw bank/merchant-tor) || true
    846 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    847   --login merchant-tor --public \
    848   --payto "$MERCHANT_PAYTO_TOR" \
    849   --name "Tor Donations Merchant" \
    850   --password $(get_credential_pw bank/merchant-tor)
    851 
    852 sudo -u libeufin-bank libeufin-bank passwd merchant-umami $(get_credential_pw bank/merchant-umami) || true
    853 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    854   --login merchant-umami --public \
    855   --payto "$MERCHANT_PAYTO_UMAMI" \
    856   --name "Umami Merchant" \
    857   --password $(get_credential_pw bank/merchant-umami)
    858 
    859 # Special bank account without a secure password
    860 sudo -u libeufin-bank libeufin-bank passwd merchant-sandbox sandbox || true
    861 taler-harness deployment provision-bank-account "${BANK_BASEURL}" \
    862   --login merchant-sandbox --public \
    863   --payto "$MERCHANT_PAYTO_SANDBOX" \
    864   --name "Sandbox Merchant" \
    865   --password sandbox
    866 
    867 # Set up exchange
    868 
    869 ##
    870 ## Configure KYC if enabled
    871 ##
    872 
    873 if [[ ${ENABLE_KYC:-0} = 1 ]]; then
    874   # KYC config
    875   if [[ ${KYC_DIALECT:-simple} = simple ]]; then
    876     source /data/setup-kyc-simple.sh
    877   elif [[ ${KYC_DIALECT:-simple} = tops ]]; then
    878     source /data/setup-kyc-tops.sh
    879   fi
    880 else
    881   rm -f /etc/taler-exchange/conf.d/sandcastle-kyc.conf
    882 fi
    883 
    884 
    885 if [[ ! -e /etc/taler-exchange/conf.d/sandcastle-$CURRENCY-coins.conf ]]; then
    886   # Only create if necessary, as each [COIN-...] section
    887   # has a unique name with a timestamp.
    888   taler-harness deployment gen-coin-config \
    889     --min-amount "${CURRENCY}:0.01" \
    890     --max-amount "${CURRENCY}:100" \
    891     --no-fees \
    892     >"/etc/taler-exchange/conf.d/sandcastle-$CURRENCY-coins.conf"
    893 else
    894   # Exchange broke backwards compatibility, fix up existing config file.
    895   sed -i 's/COIN-/COIN_/gI' "/etc/taler-exchange/conf.d/sandcastle-$CURRENCY-coins.conf"
    896 fi
    897 
    898 taler-terms-generator -i /usr/share/taler-exchange/terms/exchange-tos-v0
    899 taler-terms-generator -i /usr/share/taler-exchange/terms/exchange-pp-v0
    900 
    901 systemctl enable --now taler-exchange.target
    902 
    903 taler-harness deployment wait-taler-service taler-exchange $PROTO://$EXCHANGE_DOMAIN$PORT_SUFFIX/config
    904 taler-harness deployment wait-endpoint $PROTO://$EXCHANGE_DOMAIN$PORT_SUFFIX/management/keys
    905 
    906 sudo -u taler-exchange-offline \
    907   taler-exchange-offline \
    908   -c /etc/taler-exchange/taler-exchange.conf \
    909   download \
    910   sign \
    911   upload
    912 
    913 sudo -u taler-exchange-offline \
    914   taler-exchange-offline \
    915   enable-account "${EXCHANGE_PAYTO}" \
    916   wire-fee now "$WIRE_METHOD" "${CURRENCY}":0 "${CURRENCY}":0 \
    917   global-fee now "${CURRENCY}":0 "${CURRENCY}":0 "${CURRENCY}":0 1h 6a 0 \
    918   upload
    919 
    920 systemctl enable --now taler-exchange-offline.timer
    921 
    922 function dup_exchange_opt() {
    923   echo "$2 = $(taler-exchange-config -c /etc/taler-exchange/taler-exchange.conf -s $1 -o $2)"
    924 }
    925 
    926 #
    927 # Set up exchange auditor
    928 #
    929 
    930 if [[ $ENABLE_AUDITOR = 1 ]]; then
    931   systemctl enable --now taler-auditor.target
    932 fi
    933 
    934 # Set up merchant backend
    935 
    936 MERCHANT_BASEURL=$PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/
    937 
    938 cat <<EOF >/etc/taler-merchant/conf.d/sandcastle-merchant-terms.conf
    939 [merchant]
    940 TERMS_ETAG = merchant-tos-demo-v0
    941 TERMS_DIR = \${TALER_DATA_HOME}terms/
    942 EOF
    943 
    944 taler-terms-generator -i /usr/share/taler-merchant/terms/merchant-tos-demo-v0.en.rst -o "$(taler-merchant-config -f -s merchant -o terms_dir)"
    945 
    946 systemctl enable --now taler-merchant.target
    947 taler-harness deployment wait-taler-service taler-merchant ${MERCHANT_BASEURL}config
    948 
    949 function reset_merchant_pw() {
    950   pw=$(get_credential_pw merchant/$1)
    951   sudo -u taler-merchant-httpd taler-merchant-passwd --instance "$1" "$pw"
    952   if [[ $? -eq 2 ]]; then
    953     echo "Instance $1 does not exist" >&2
    954     return 2
    955   fi
    956   if [[ $? -ne 0 ]]; then
    957     echo "Failed to reset password for merchant instance $1" >&2
    958     exit 1
    959   fi
    960 }
    961 
    962 # FIXME: Move this into a harness tool (that just reads a config file)?
    963 
    964 instance_missing=no
    965 reset_merchant_pw admin || instance_missing=yes
    966 if [[ $instance_missing = yes ]]; then
    967   taler-harness deployment provision-merchant-instance \
    968     ${MERCHANT_BASEURL} \
    969     --management-token "secret-token:none" \
    970     --instance-password $(get_credential_pw merchant/admin) \
    971     --name Merchant \
    972     --id admin \
    973     --payto "$MERCHANT_PAYTO_ADMIN"
    974 fi
    975 
    976 ADMIN_TOK=$(taler-harness merchant token ${MERCHANT_BASEURL} admin --password $(get_credential_pw merchant/admin))
    977 
    978 instance_missing=no
    979 reset_merchant_pw pos || instance_missing=yes
    980 if [[ $instance_missing = yes ]]; then
    981   taler-harness deployment provision-merchant-instance \
    982     ${MERCHANT_BASEURL} \
    983     --management-token $ADMIN_TOK \
    984     --instance-password $(get_credential_pw merchant/pos) \
    985     --name "POS Merchant" \
    986     --id pos \
    987     --payto "$MERCHANT_PAYTO_POS"
    988 fi
    989 
    990 instance_missing=no
    991 reset_merchant_pw testing || instance_missing=yes
    992 if [[ $instance_missing = yes ]]; then
    993   taler-harness deployment provision-merchant-instance \
    994     ${MERCHANT_BASEURL} \
    995     --management-token $ADMIN_TOK \
    996     --instance-password $(get_credential_pw merchant/testing) \
    997     --name "Testing Merchant" \
    998     --id testing \
    999     --payto "$MERCHANT_PAYTO_TESTING"
   1000 fi
   1001 
   1002 instance_missing=no
   1003 reset_merchant_pw blog || instance_missing=yes
   1004 if [[ $instance_missing = yes ]]; then
   1005   taler-harness deployment provision-merchant-instance \
   1006     ${MERCHANT_BASEURL} \
   1007     --management-token $ADMIN_TOK \
   1008     --instance-password $(get_credential_pw merchant/blog) \
   1009     --name "Blog Merchant" \
   1010     --id blog \
   1011     --payto "$MERCHANT_PAYTO_BLOG"
   1012 fi
   1013 
   1014 instance_missing=no
   1015 reset_merchant_pw gnunet || instance_missing=yes
   1016 if [[ $instance_missing = yes ]]; then
   1017   taler-harness deployment provision-merchant-instance \
   1018     ${MERCHANT_BASEURL} \
   1019     --management-token $ADMIN_TOK \
   1020     --instance-password $(get_credential_pw merchant/gnunet) \
   1021     --name "GNUnet Merchant" \
   1022     --id gnunet \
   1023     --payto "$MERCHANT_PAYTO_GNUNET"
   1024 fi
   1025 
   1026 instance_missing=no
   1027 reset_merchant_pw taler || instance_missing=yes
   1028 if [[ $instance_missing = yes ]]; then
   1029   taler-harness deployment provision-merchant-instance \
   1030     ${MERCHANT_BASEURL} \
   1031     --management-token $ADMIN_TOK \
   1032     --instance-password $(get_credential_pw merchant/taler) \
   1033     --name "Taler Merchant" \
   1034     --id taler \
   1035     --payto "$MERCHANT_PAYTO_TALER"
   1036 fi
   1037 
   1038 instance_missing=no
   1039 reset_merchant_pw tor || instance_missing=yes
   1040 if [[ $instance_missing = yes ]]; then
   1041   taler-harness deployment provision-merchant-instance \
   1042     ${MERCHANT_BASEURL} \
   1043     --management-token $ADMIN_TOK \
   1044     --instance-password $(get_credential_pw merchant/tor) \
   1045     --name "Tor Merchant" \
   1046     --id tor \
   1047     --payto "$MERCHANT_PAYTO_TOR"
   1048 fi
   1049 
   1050 instance_missing=no
   1051 reset_merchant_pw umami || instance_missing=yes
   1052 if [[ $instance_missing = yes ]]; then
   1053   taler-harness deployment provision-merchant-instance \
   1054     ${MERCHANT_BASEURL} \
   1055     --management-token $ADMIN_TOK \
   1056     --instance-password $(get_credential_pw merchant/umami) \
   1057     --name "Umami Merchant" \
   1058     --id umami \
   1059     --payto "$MERCHANT_PAYTO_UMAMI"
   1060 fi
   1061 
   1062 # Special instance with fixed "sandbox" password
   1063 sudo -u taler-merchant-httpd taler-merchant-passwd --instance sandbox sandbox || true
   1064 taler-harness deployment provision-merchant-instance \
   1065   ${MERCHANT_BASEURL} \
   1066   --management-token $ADMIN_TOK \
   1067   --instance-password sandbox \
   1068   --name "sandbox merchant" \
   1069   --id sandbox \
   1070   --payto "$MERCHANT_PAYTO_SANDBOX"
   1071 
   1072 # token families needed by demo blog
   1073 
   1074 langs=(de en ar zh fr hi it ja ko pt pt_BR ru es sv tr uk)
   1075 valid_before_ts=$(date -u +%s -d '+1 year') # one year later
   1076 duration_us=$((30 * 24 * 60 * 60 * 1000000)) # 30 days
   1077 validity_granularity_us=$((24 * 60 * 60 * 1000000)) # 1 day
   1078 
   1079 # FIXME: Move this into a harness tool?
   1080 for lang in "${langs[@]}"; do
   1081     curl -X POST "${MERCHANT_BASEURL}instances/blog/private/tokenfamilies" \
   1082          -H "Authorization: Bearer secret-token:$(get_credential_pw merchant/blog)" \
   1083          -H "Content-Type: application/json" \
   1084          --data-raw "{
   1085   \"kind\": \"subscription\",
   1086   \"slug\": \"blog_abo_${lang}\",
   1087   \"name\": \"One month of access (${lang})\",
   1088   \"description\": \"One month of access (${lang})\",
   1089   \"description_i18n\": {
   1090     \"de\": \"Ein monat lang Zugang zu den Artikeln\",
   1091     \"en\": \"One month of access to articles\",
   1092     \"fr\": \"Un mois d'accès aux articles\",
   1093     \"es\": \"Un mes de acceso a los artículos\"
   1094   },
   1095   \"valid_before\": { \"t_s\": ${valid_before_ts} },
   1096   \"duration\": { \"d_us\": ${duration_us} },
   1097   \"validity_granularity\": { \"d_us\": ${validity_granularity_us} }
   1098 }"
   1099 done
   1100 
   1101 
   1102 
   1103 # Set up Donau
   1104 
   1105 cat <<EOF >/etc/donau/conf.d/sandcastle.conf
   1106 [donau]
   1107 CURRENCY = $CURRENCY
   1108 LEGAL_DOMAIN = Gnuland
   1109 EXPIRE_LEGAL_YEARS = 3
   1110 # We don't do the token yet, as the merchant doesn't support
   1111 # authenticating with donau.
   1112 # ADMIN_BEARER_TOKEN = secret-token:secret
   1113 EOF
   1114 
   1115 donau-dbconfig
   1116 
   1117 if [[ ! -e /etc/donau/conf.d/sandcastle-$CURRENCY-units.conf ]]; then
   1118   # Only create if necessary
   1119   taler-harness deployment gen-doco-config \
   1120     --min-amount "${CURRENCY}:0.01" \
   1121     --max-amount "${CURRENCY}:100" \
   1122     >"/etc/donau/conf.d/sandcastle-$CURRENCY-units.conf"
   1123 fi
   1124 
   1125 systemctl enable --now donau.target
   1126 
   1127 DONAU_BASE_URL=$PROTO://$DONAU_DOMAIN$PORT_SUFFIX/
   1128 
   1129 taler-harness deployment wait-taler-service donau ${DONAU_BASE_URL}config
   1130 
   1131 if [[ ${ENABLE_TALDIR:-0} == 1 ]]; then
   1132   # Mailbox and Directory
   1133   mkdir -p /etc/taler-directory
   1134   cp /usr/share/taler-directory/taldir.conf.example /etc/taler-directory/taler-directory.conf
   1135   taler-directory-dbconfig
   1136   systemctl enable --now taler-directory.service
   1137 fi
   1138 
   1139 if [[ ${ENABLE_MAILBOX:-0} == 1 ]]; then
   1140   mkdir -p /etc/taler-mailbox
   1141   cp /usr/share/taler-mailbox/mailbox.conf.example /etc/taler-mailbox/taler-mailbox.conf
   1142   sed -i 's/localhost:11000/localhost:12000/' /etc/taler-mailbox/taler-mailbox.conf
   1143   taler-mailbox-dbconfig
   1144   systemctl enable --now taler-mailbox.service
   1145 fi
   1146 
   1147 GNUNET_TOK=$(taler-harness merchant token ${MERCHANT_BASEURL}instances/gnunet/ gnunet --password $(get_credential_pw merchant/gnunet))
   1148 taler-harness deployment provision-merchant-donau \
   1149   --merchant-auth-token $GNUNET_TOK \
   1150   --merchant-base-url $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/gnunet/ \
   1151   --donau-base-url $DONAU_BASE_URL \
   1152   --donau-auth-token secret-token:secret \
   1153   --currency $CURRENCY
   1154 
   1155 TALER_TOK=$(taler-harness merchant token ${MERCHANT_BASEURL}instances/taler/ taler --password $(get_credential_pw merchant/taler))
   1156 taler-harness deployment provision-merchant-donau \
   1157   --merchant-auth-token $TALER_TOK \
   1158   --merchant-base-url $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/taler/ \
   1159   --donau-base-url $DONAU_BASE_URL \
   1160   --donau-auth-token secret-token:secret \
   1161   --currency $CURRENCY
   1162 
   1163 TOR_TOK=$(taler-harness merchant token ${MERCHANT_BASEURL}instances/tor/ tor --password $(get_credential_pw merchant/tor))
   1164 taler-harness deployment provision-merchant-donau \
   1165   --merchant-auth-token $TOR_TOK \
   1166   --merchant-base-url $PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/tor/ \
   1167   --donau-base-url $DONAU_BASE_URL \
   1168   --donau-auth-token secret-token:secret \
   1169   --currency $CURRENCY
   1170 
   1171 UMAMI_TOK=$(taler-harness merchant token ${MERCHANT_BASEURL}instances/umami/ umami --password $(get_credential_pw merchant/umami))
   1172 
   1173 # Now we set up the taler-merchant-demos
   1174 
   1175 systemctl enable --now taler-demo-landing
   1176 systemctl enable --now taler-demo-blog
   1177 systemctl enable --now taler-demo-donations
   1178 
   1179 # Turnstile (drupal/php)
   1180 
   1181 
   1182 systemctl enable --now php8.4-fpm
   1183 
   1184 DRUPAL_DB_PW=$(get_credential_pw db/drupal)
   1185 DRUPAL_ADMIN_PW=$(get_credential_pw drupal/admin)
   1186 
   1187 sudo -i -u postgres psql postgres -c "CREATE ROLE drupal WITH login;" || true
   1188 sudo -i -u postgres psql postgres -c "ALTER ROLE drupal password '$DRUPAL_DB_PW';"
   1189 sudo -u postgres createdb drupal --owner=drupal || true
   1190 
   1191 # Needed by PHP's composer
   1192 export HOME=/root
   1193 
   1194 # FIXME: Would probably be better to checkout output of
   1195 # drush status --fields=bootstrap --format=string
   1196 
   1197 cd /talerdata/
   1198 if [[ ! -e /talerdata/sandcastle-drupal ]]; then
   1199   composer create-project drupal/recommended-project:^10 sandcastle-drupal
   1200   cd /talerdata/sandcastle-drupal
   1201   composer require drush/drush
   1202   # This can take a ridiculous amount of time!
   1203   COMPOSER_PROCESS_TIMEOUT=0 composer exec -- drush site-install demo_umami --account-name=admin --account-pass=$DRUPAL_ADMIN_PW --account-mail=admin@localhost --db-url=pgsql://drupal:$DRUPAL_DB_PW@localhost/drupal --site-name=SandcastleUmami --yes
   1204 fi
   1205 
   1206 chown -R www-data:www-data /talerdata/sandcastle-drupal/
   1207 
   1208 ln -sf /opt/turnstile /talerdata/sandcastle-drupal/web/modules/taler_turnstile
   1209 
   1210 snip=$(cat <<'EOF'
   1211 $settings['reverse_proxy'] = TRUE;
   1212 $settings['reverse_proxy_addresses'] = ['127.0.0.1', '10.0.0.0/8'];
   1213 $settings['trusted_host_patterns'] = ['.*'];
   1214 EOF
   1215 )
   1216 
   1217 update_config_block /talerdata/sandcastle-drupal/web/sites/default/settings.php SANDCASTLE "$snip"
   1218 
   1219 cd /talerdata/sandcastle-drupal
   1220 composer exec -- drush upwd admin $DRUPAL_ADMIN_PW
   1221 composer exec -- drush en taler_turnstile
   1222 composer exec -- drush config:set taler_turnstile.settings access_token "$UMAMI_TOK" --yes
   1223 composer exec -- drush config:set taler_turnstile.settings payment_backend_url "$PROTO://$MERCHANT_DOMAIN$PORT_SUFFIX/instances/umami/" --yes
   1224 composer exec -- drush config:set --input-format=yaml taler_turnstile.settings enabled_content_types '["article", "recipe"]' --yes
   1225 
   1226 snip=$(cat <<'EOF' | sed "s/@CURRENCY@/$CURRENCY/"
   1227 $storage = \Drupal::entityTypeManager()->getStorage('taler_turnstile_price_category');
   1228 $e = $storage->create(['id'=>"normal", 'label'=>'Normal', 'description' => "Normal Article Price", 'prices' => ['%none%' => ['@CURRENCY@' => '0.3']]]);
   1229 try {
   1230   $e->save();
   1231 } catch (Exception $ex) {
   1232   echo $ex->getMessage();
   1233   echo "\n";
   1234 }
   1235 EOF
   1236 )
   1237 composer exec -- drush php:eval "$snip"
   1238 
   1239 snip=$(cat <<'EOF'
   1240 $prcat_storage = \Drupal::entityTypeManager()->getStorage('taler_turnstile_price_category');
   1241 $node_storage = \Drupal::entityTypeManager()->getStorage('node');
   1242 $prcat = $prcat_storage->load('normal');
   1243 $nodes = $node_storage->loadByProperties(['type'=> ['article', 'recipe']]);
   1244 foreach ($nodes as $k => $node) {
   1245   echo 'updating node ' . $k . "\n";
   1246   if (!$node->hasField('field_taler_turnstile_prcat')) {
   1247     echo 'prcat missing' . "\n";
   1248     continue;
   1249   }
   1250   $node->set('field_taler_turnstile_prcat', $prcat);
   1251   $node->save();
   1252 }
   1253 EOF
   1254 )
   1255 composer exec -- drush php:eval "$snip"
   1256 
   1257 composer exec -- drush cr
   1258 
   1259 cd /
   1260 
   1261 # FIXME: Maybe do some taler-wallet-cli test?
   1262 # FIXME: How do we report errors occurring during the setup script?