diff --git a/contrib/Makefile.am b/contrib/Makefile.am index 99927e7e6..9d1fe6cf4 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -70,7 +70,8 @@ EXTRA_DIST = \ $(rdata_DATA) \ coverage.sh \ gnunet.tag \ - microhttpd.tag + microhttpd.tag \ + packages # Change the set of supported languages here. You should # also update tos'XX'data and EXTRA_DIST accordingly. diff --git a/contrib/gana b/contrib/gana index 85736484c..f9ea79a6a 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit 85736484cb0da26aded705ebb1e944e8bb1b8504 +Subproject commit f9ea79a6aae074928f44960f69bc3c2d0c9c7e1d diff --git a/contrib/packages/fedora/etc-libtalerexchange/taler/overrides.conf b/contrib/packages/fedora/etc-libtalerexchange/taler/overrides.conf new file mode 100644 index 000000000..60296ead4 --- /dev/null +++ b/contrib/packages/fedora/etc-libtalerexchange/taler/overrides.conf @@ -0,0 +1 @@ +# This configuration will be changed by tooling. Do not touch it manually. diff --git a/contrib/packages/fedora/etc-libtalerexchange/taler/taler.conf b/contrib/packages/fedora/etc-libtalerexchange/taler/taler.conf new file mode 100644 index 000000000..1c86ccc36 --- /dev/null +++ b/contrib/packages/fedora/etc-libtalerexchange/taler/taler.conf @@ -0,0 +1,49 @@ +# Main entry point for the GNU Taler configuration. +# +# Structure: +# - taler.conf is the main configuration entry point +# used by all Taler components (the file you are currently +# looking at. +# - overrides.conf contains configuration overrides that are +# set by some tools that help with the configuration, +# and should not be edited by humans. Comments in this file +# are not preserved. +# - conf.d/ contains configuration files for +# Taler components, which can be read by all +# users of the system and are included by the main +# configuration. +# - secrets/ contains configuration snippets +# with secrets for particular services. +# These files should have restrictive permissions +# so that only users of the relevant services +# can read it. All files in it should end with +# ".secret.conf". + +[taler] + +# Currency of the Taler deployment. This setting applies to all Taler +# components that only support a single currency. +#currency = KUDOS + +# Smallest currency unit handled by the underlying bank system. Taler payments +# can make payments smaller than this units, but interactions with external +# systems is always rounded to this unit. +#currency_round_unit = KUDOS:0.01 + +# Monthly amount that mandatorily triggers an AML check +#AML_THRESHOLD = KUDOS:10000000 + +[paths] + +TALER_HOME = /var/lib/taler +TALER_RUNTIME_DIR = /run/taler +TALER_CACHE_HOME = /var/cache/taler +TALER_CONFIG_HOME = /etc/taler +TALER_DATA_HOME = /var/lib/taler + + +# Inline configurations from all Taler components. +@inline-matching@ conf.d/*.conf + +# Overrides from tools that help with configuration. +@inline@ overrides.conf diff --git a/contrib/packages/fedora/etc-taler-auditor/apache2/sites-available/taler-auditor.conf b/contrib/packages/fedora/etc-taler-auditor/apache2/sites-available/taler-auditor.conf new file mode 100644 index 000000000..f68c59558 --- /dev/null +++ b/contrib/packages/fedora/etc-taler-auditor/apache2/sites-available/taler-auditor.conf @@ -0,0 +1,4 @@ + +ProxyPass "unix:/var/lib/taler-auditor/auditor.sock|http://example.com/" +RequestHeader add "X-Forwarded-Proto" "https" + diff --git a/contrib/packages/fedora/etc-taler-auditor/nginx/sites-available/taler-auditor b/contrib/packages/fedora/etc-taler-auditor/nginx/sites-available/taler-auditor new file mode 100644 index 000000000..f74035d53 --- /dev/null +++ b/contrib/packages/fedora/etc-taler-auditor/nginx/sites-available/taler-auditor @@ -0,0 +1,18 @@ +server { + + listen 80; + listen [::]:80; + + server_name localhost; + + access_log /var/log/nginx/auditor.log; + error_log /var/log/nginx/auditor.err; + + location /taler-auditor/ { + proxy_pass http://unix:/var/lib/taler-auditor/auditor.sock; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host "localhost"; + #proxy_set_header X-Forwarded-Proto "https"; + } +} \ No newline at end of file diff --git a/contrib/packages/fedora/etc-taler-auditor/taler/conf.d/auditor-system.conf b/contrib/packages/fedora/etc-taler-auditor/taler/conf.d/auditor-system.conf new file mode 100644 index 000000000..3d3aef33a --- /dev/null +++ b/contrib/packages/fedora/etc-taler-auditor/taler/conf.d/auditor-system.conf @@ -0,0 +1,12 @@ +# Read secret sections into configuration, but only +# if we have permission to do so. +@inline-secret@ auditordb-postgres ../secrets/auditor-db.secret.conf + +[auditor] +# Debian package is configured to use a reverse proxy with a UNIX +# domain socket. See nginx/apache configuration files. +SERVE = UNIX +UNIXPATH = /var/lib/taler-auditor/auditor.sock + +# Only supported database is Postgres right now. +DATABASE = postgres diff --git a/contrib/packages/fedora/etc-taler-auditor/taler/secrets/auditor-db.secret.conf b/contrib/packages/fedora/etc-taler-auditor/taler/secrets/auditor-db.secret.conf new file mode 100644 index 000000000..b81bb817f --- /dev/null +++ b/contrib/packages/fedora/etc-taler-auditor/taler/secrets/auditor-db.secret.conf @@ -0,0 +1,10 @@ +# Database configuration for the Taler auditor. + +[auditordb-postgres] + +# Typically, there should only be a single line here, of the form: + +CONFIG=postgres:///DATABASE + +# The details of the URI depend on where the database lives and how +# access control was configured. diff --git a/contrib/packages/fedora/etc-taler-exchange/apache2/sites-available/taler-exchange.conf b/contrib/packages/fedora/etc-taler-exchange/apache2/sites-available/taler-exchange.conf new file mode 100644 index 000000000..3ec14feb2 --- /dev/null +++ b/contrib/packages/fedora/etc-taler-exchange/apache2/sites-available/taler-exchange.conf @@ -0,0 +1,4 @@ + +ProxyPass "unix:/run/taler/exchange-httpd/exchange-http.sock|http://example.com/" +RequestHeader add "X-Forwarded-Proto" "https" + diff --git a/contrib/packages/fedora/etc-taler-exchange/nginx/sites-available/taler-exchange b/contrib/packages/fedora/etc-taler-exchange/nginx/sites-available/taler-exchange new file mode 100644 index 000000000..9b61a32df --- /dev/null +++ b/contrib/packages/fedora/etc-taler-exchange/nginx/sites-available/taler-exchange @@ -0,0 +1,17 @@ +server { + listen 80; + listen [::]:80; + + server_name localhost; + + access_log /var/log/nginx/exchange.log; + error_log /var/log/nginx/exchange.err; + + location /taler-exchange/ { + proxy_pass http://unix:/run/taler/exchange-httpd/exchange-http.sock:/; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-Host "localhost"; + #proxy_set_header X-Forwarded-Proto "https"; + } +} diff --git a/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-business.conf b/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-business.conf new file mode 100644 index 000000000..d5938f2b1 --- /dev/null +++ b/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-business.conf @@ -0,0 +1,50 @@ +# Configuration for business-level aspects of the exchange. + +[exchange] + +# Here you MUST add the master public key of the offline system +# which you can get using `taler-exchange-offline setup`. +# This is just an example, your key will be different! +# MASTER_PUBLIC_KEY = YE6Q6TR1EDB7FD0S68TGDZGF1P0GHJD2S0XVV8R2S62MYJ6HJ4ZG +# MASTER_PUBLIC_KEY = + +# Publicly visible base URL of the exchange. +# BASE_URL = https://example.com/ +# BASE_URL = + +# Here you MUST configure the amount above which transactions are +# always subject to manual AML review. +# AML_THRESHOLD = + +# Attribute encryption key for storing attributes encrypted +# in the database. Should be a high-entropy nonce. +ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE + +# For your terms of service and privacy policy, you should specify +# an Etag that must be updated whenever there are significant +# changes to either document. The format is up to you, what matters +# is that the value is updated and never re-used. See the HTTP +# specification on Etags. +# TERMS_ETAG = +# PRIVACY_ETAG = + +SERVE = unix +UNIXPATH_MODE = 666 + +# Bank accounts used by the exchange should be specified here: +[exchange-account-1] + +ENABLE_CREDIT = NO +ENABLE_DEBIT = NO + +# Account identifier in the form of an RFC-8905 payto:// URI. +# For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME +# Make sure to URL-encode spaces in $NAME! +PAYTO_URI = + +# Credentials to access the account are in a separate +# config file with restricted permissions. +@inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf + + + diff --git a/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-coins.conf b/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-coins.conf new file mode 100644 index 000000000..8294525cb --- /dev/null +++ b/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-coins.conf @@ -0,0 +1,33 @@ +# +# This configuration file specifies the various denominations offered by your +# exchange. +# +# Each denomination must be specified in a sections starting with +# "coin_". +# +# What follows is an example. +# + +# [coin_FOO] +## Actual value of the coin +#VALUE = KUDOS:1 + +## How long will one key be used for withdrawals? +#DURATION_WITHDRAW = 7 days + +## How long do users have to spend their coins? +#DURATION_SPEND = 2 years + +## How long does the exchange keep the proofs around for legal disputes? +#DURATION_LEGAL = 6 years + +## Fees charged. Note that for the lowest denomination, the +## fee must precisely be the lowest denomination, or zero. +#FEE_WITHDRAW = KUDOS:0 +#FEE_DEPOSIT = KUDOS:0 +#FEE_REFRESH = KUDOS:0 +#FEE_REFUND = KUDOS:0 + +## How long should the RSA keys be. Do not change unless you really know +## what you are doing (consult your local cryptographer first!). +#RSA_KEYSIZE = 2048 diff --git a/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-system.conf b/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-system.conf new file mode 100644 index 000000000..4ad7e06f6 --- /dev/null +++ b/contrib/packages/fedora/etc-taler-exchange/taler/conf.d/exchange-system.conf @@ -0,0 +1,13 @@ +# Configuration settings for system parameters of the exchange. + +# Read secret sections into configuration, but only +# if we have permission to do so. +@inline-secret@ exchangedb-postgres ../secrets/exchange-db.secret.conf + +[exchange] + +# Only supported database is Postgres right now. +DATABASE = postgres + + + diff --git a/contrib/packages/fedora/etc-taler-exchange/taler/secrets/exchange-accountcredentials-1.secret.conf b/contrib/packages/fedora/etc-taler-exchange/taler/secrets/exchange-accountcredentials-1.secret.conf new file mode 100644 index 000000000..8c8d14320 --- /dev/null +++ b/contrib/packages/fedora/etc-taler-exchange/taler/secrets/exchange-accountcredentials-1.secret.conf @@ -0,0 +1,17 @@ +# This file contains the secret credentials +# to access the Taler Wire Gateway API (usually +# provided by LibEuFin) for the exchange accounts. +# +# Each exchange-account-* section should have a matching +# exchange-accountcredentials-* section here. +# +# Each of those sections must be imported via @inline-secret@, +# usually in conf.d/exchange-business.conf. + +[exchange-accountcredentials-1] + +wire_gateway_auth_method = basic +password = +username = +wire_gateway_url = + diff --git a/contrib/packages/fedora/etc-taler-exchange/taler/secrets/exchange-db.secret.conf b/contrib/packages/fedora/etc-taler-exchange/taler/secrets/exchange-db.secret.conf new file mode 100644 index 000000000..a7a727b62 --- /dev/null +++ b/contrib/packages/fedora/etc-taler-exchange/taler/secrets/exchange-db.secret.conf @@ -0,0 +1,10 @@ +# Database configuration for the Taler exchange. + +[exchangedb-postgres] + +# Typically, there should only be a single line here, of the form: + +# CONFIG=postgres:///DATABASE + +# The details of the URI depend on where the database lives and how +# access control was configured. diff --git a/contrib/packages/fedora/taler-auditor.taler-auditor-httpd.service b/contrib/packages/fedora/taler-auditor.taler-auditor-httpd.service new file mode 100644 index 000000000..9aefab641 --- /dev/null +++ b/contrib/packages/fedora/taler-auditor.taler-auditor-httpd.service @@ -0,0 +1,12 @@ +[Unit] +Description=GNU Taler payment system auditor REST API +After=postgres.service network.target + +[Service] +User=taler-auditor-httpd +Type=simple +Restart=on-failure +ExecStart=/usr/bin/taler-auditor-httpd -c /etc/taler/taler.conf + +[Install] +WantedBy=multi-user.target diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-aggregator.service b/contrib/packages/fedora/taler-exchange.taler-exchange-aggregator.service new file mode 100644 index 000000000..246cad5c1 --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-aggregator.service @@ -0,0 +1,18 @@ +[Unit] +Description=GNU Taler payment system exchange aggregator service +PartOf=taler-exchange.target +After=postgres.service + +[Service] +User=taler-exchange-aggregator +Type=simple +Restart=always +RestartSec=1s +ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice +RuntimeMaxSec=3600s diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-aggregator@.service b/contrib/packages/fedora/taler-exchange.taler-exchange-aggregator@.service new file mode 100644 index 000000000..bfc44a9a9 --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-aggregator@.service @@ -0,0 +1,17 @@ +[Unit] +Description=GNU Taler payment system exchange aggregator service +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-aggregator +Type=simple +Restart=always +RestartSec=1s +ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice +RuntimeMaxSec=3600s diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-closer.service b/contrib/packages/fedora/taler-exchange.taler-exchange-closer.service new file mode 100644 index 000000000..97a385c13 --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-closer.service @@ -0,0 +1,18 @@ +[Unit] +Description=GNU Taler payment system exchange closer service +PartOf=taler-exchange.target +After=network.target postgres.service + +[Service] +User=taler-exchange-closer +Type=simple +Restart=always +RestartSec=1s +ExecStart=/usr/bin/taler-exchange-closer -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice +RuntimeMaxSec=3600s diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-expire.service b/contrib/packages/fedora/taler-exchange.taler-exchange-expire.service new file mode 100644 index 000000000..250f210fe --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-expire.service @@ -0,0 +1,18 @@ +[Unit] +Description=GNU Taler payment system exchange expire service +PartOf=taler-exchange.target +After=postgres.service + +[Service] +User=taler-exchange-expire +Type=simple +Restart=always +RestartSec=1s +ExecStart=/usr/bin/taler-exchange-expire -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice +RuntimeMaxSec=3600s diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-httpd.service b/contrib/packages/fedora/taler-exchange.taler-exchange-httpd.service new file mode 100644 index 000000000..3671bdc7d --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-httpd.service @@ -0,0 +1,33 @@ +[Unit] +Description=GNU Taler payment system exchange REST API +AssertPathExists=/run/taler/exchange-httpd +Requires=taler-exchange-httpd.socket taler-exchange-secmod-cs.service taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service +After=postgres.service network.target taler-exchange-secmod-cs.service taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-httpd +Type=simple + +# Depending on the configuration, the service process kills itself and then +# needs to be restarted. Thus no significant delay on restarts. +Restart=always +RestartSec=1ms + +# Disable the service if more than 5 restarts are encountered within 5s. +# These are usually the systemd defaults, but can be overwritten, thus we set +# them here explicitly, as the exchange code assumes StartLimitInterval +# to be >=5s. +StartLimitBurst=5 +StartLimitInterval=5s + +ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=no +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice + +[Install] +WantedBy=multi-user.target diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-httpd@.service b/contrib/packages/fedora/taler-exchange.taler-exchange-httpd@.service new file mode 100644 index 000000000..e0246899c --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-httpd@.service @@ -0,0 +1,27 @@ +% This is a systemd service template. +[Unit] +Description=GNU Taler payment system exchange REST API at %I +AssertPathExists=/run/taler/exchange-httpd +Requires=taler-exchange-httpd@%i.socket taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service +After=postgres.service network.target taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-httpd +Type=simple +# Depending on the configuration, the service suicides and then +# needs to be restarted. +Restart=always +# Do not dally on restarts. +RestartSec=1ms +EnvironmentFile=/etc/environment +ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=no +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice + +[Install] +WantedBy=multi-user.target diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-cs.service b/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-cs.service new file mode 100644 index 000000000..3b5e0745d --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-cs.service @@ -0,0 +1,18 @@ +[Unit] +Description=GNU Taler payment system exchange CS security module +AssertPathExists=/run/taler/exchange-secmod-cs +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-secmod-cs +Type=simple +Restart=always +RestartSec=100ms +ExecStart=/usr/bin/taler-exchange-secmod-cs -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=no +PrivateDevices=yes +ProtectSystem=full +IPAddressDeny=any +Slice=taler-exchange.slice diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-eddsa.service b/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-eddsa.service new file mode 100644 index 000000000..e8fba1736 --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-eddsa.service @@ -0,0 +1,19 @@ +[Unit] +Description=GNU Taler payment system exchange EdDSA security module +AssertPathExists=/run/taler/exchange-secmod-eddsa +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-secmod-eddsa +Type=simple +Restart=always +RestartSec=100ms +ExecStart=/usr/bin/taler-exchange-secmod-eddsa -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=no +PrivateDevices=yes +ProtectSystem=full +IPAddressDeny=any +Slice=taler-exchange.slice + diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-rsa.service b/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-rsa.service new file mode 100644 index 000000000..10a9585a7 --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-secmod-rsa.service @@ -0,0 +1,18 @@ +[Unit] +Description=GNU Taler payment system exchange RSA security module +AssertPathExists=/run/taler/exchange-secmod-rsa +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-secmod-rsa +Type=simple +Restart=always +RestartSec=100ms +ExecStart=/usr/bin/taler-exchange-secmod-rsa -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=no +PrivateDevices=yes +ProtectSystem=full +IPAddressDeny=any +Slice=taler-exchange.slice diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-transfer.service b/contrib/packages/fedora/taler-exchange.taler-exchange-transfer.service new file mode 100644 index 000000000..e26af20d0 --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-transfer.service @@ -0,0 +1,18 @@ +[Unit] +Description=Taler Exchange Transfer Service +After=network.target postgres.service +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-wire +Type=simple +Restart=always +RestartSec=1s +ExecStart=/usr/bin/taler-exchange-transfer -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice +RuntimeMaxSec=3600s diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-wirewatch.service b/contrib/packages/fedora/taler-exchange.taler-exchange-wirewatch.service new file mode 100644 index 000000000..7b74737b7 --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-wirewatch.service @@ -0,0 +1,18 @@ +[Unit] +Description=GNU Taler payment system exchange wirewatch service +After=network.target postgres.service +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-wire +Type=simple +Restart=always +RestartSec=1s +RuntimeMaxSec=3600s +ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice diff --git a/contrib/packages/fedora/taler-exchange.taler-exchange-wirewatch@.service b/contrib/packages/fedora/taler-exchange.taler-exchange-wirewatch@.service new file mode 100644 index 000000000..85bb9268b --- /dev/null +++ b/contrib/packages/fedora/taler-exchange.taler-exchange-wirewatch@.service @@ -0,0 +1,18 @@ +[Unit] +Description=GNU Taler payment system exchange wirewatch service +After=network.target +PartOf=taler-exchange.target + +[Service] +User=taler-exchange-wire +Type=simple +Restart=always +RestartSec=1s +ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf +StandardOutput=journal +StandardError=journal +PrivateTmp=yes +PrivateDevices=yes +ProtectSystem=full +Slice=taler-exchange.slice +RuntimeMaxSec=3600s diff --git a/contrib/uncrustify_precommit b/contrib/uncrustify_precommit index 853c8125d..c10bc2673 100755 --- a/contrib/uncrustify_precommit +++ b/contrib/uncrustify_precommit @@ -4,7 +4,7 @@ exec 1>&2 RET=0 -changed=$(git diff --cached --name-only | grep -v mustach) +changed=$(git diff --cached --name-only | grep -v mustach | grep -v templating/test./) crustified="" for f in $changed; @@ -28,7 +28,7 @@ done if [ $RET = 1 ]; then echo "Run" - echo "uncrustify --no-backup -c uncrustify.cfg ${crustified}" + echo "uncrustify --replace -c uncrustify.cfg ${crustified}" echo "before committing." fi exit $RET diff --git a/debian/etc-taler-exchange/taler/conf.d/exchange-business.conf b/debian/etc-taler-exchange/taler/conf.d/exchange-business.conf index 89583d5be..d5938f2b1 100644 --- a/debian/etc-taler-exchange/taler/conf.d/exchange-business.conf +++ b/debian/etc-taler-exchange/taler/conf.d/exchange-business.conf @@ -12,6 +12,14 @@ # BASE_URL = https://example.com/ # BASE_URL = +# Here you MUST configure the amount above which transactions are +# always subject to manual AML review. +# AML_THRESHOLD = + +# Attribute encryption key for storing attributes encrypted +# in the database. Should be a high-entropy nonce. +ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE + # For your terms of service and privacy policy, you should specify # an Etag that must be updated whenever there are significant # changes to either document. The format is up to you, what matters @@ -26,14 +34,17 @@ UNIXPATH_MODE = 666 # Bank accounts used by the exchange should be specified here: [exchange-account-1] -enable_credit = no -enable_debit = no +ENABLE_CREDIT = NO +ENABLE_DEBIT = NO # Account identifier in the form of an RFC-8905 payto:// URI. # For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME # Make sure to URL-encode spaces in $NAME! -payto_uri = +PAYTO_URI = # Credentials to access the account are in a separate # config file with restricted permissions. @inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf + + + diff --git a/debian/etc-taler-exchange/taler/conf.d/exchange-system.conf b/debian/etc-taler-exchange/taler/conf.d/exchange-system.conf index 75c670f71..4ad7e06f6 100644 --- a/debian/etc-taler-exchange/taler/conf.d/exchange-system.conf +++ b/debian/etc-taler-exchange/taler/conf.d/exchange-system.conf @@ -8,3 +8,6 @@ # Only supported database is Postgres right now. DATABASE = postgres + + + diff --git a/doc/Makefile.am b/doc/Makefile.am index 0b8cd63dc..6475ea415 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -24,6 +24,7 @@ man_MANS = \ prebuilt/man/taler-exchange-drain.1 \ prebuilt/man/taler-exchange-expire.1 \ prebuilt/man/taler-exchange-httpd.1 \ + prebuilt/man/taler-exchange-kyc-aml-pep-trigger.1 \ prebuilt/man/taler-exchange-kyc-tester.1 \ prebuilt/man/taler-exchange-offline.1 \ prebuilt/man/taler-exchange-router.1\ diff --git a/doc/prebuilt b/doc/prebuilt index 8452f991d..66e99d09d 160000 --- a/doc/prebuilt +++ b/doc/prebuilt @@ -1 +1 @@ -Subproject commit 8452f991dd967328207fab52a99beb19e2cb4dff +Subproject commit 66e99d09d4351bb6e6c5fd442f14ec7cf1363a81 diff --git a/src/auditor/taler-auditor-exchange.c b/src/auditor/taler-auditor-exchange.c index 04181ce3f..b34d14842 100644 --- a/src/auditor/taler-auditor-exchange.c +++ b/src/auditor/taler-auditor-exchange.c @@ -213,7 +213,7 @@ main (int argc, ? "Could not remove exchange from database: entry already absent\n" : "Could not add exchange to database: entry already exists\n"); TALER_AUDITORDB_plugin_unload (adb); - return EXIT_FAILURE; + return EXIT_SUCCESS; } } TALER_AUDITORDB_plugin_unload (adb); diff --git a/src/benchmark/benchmark-cs.conf b/src/benchmark/benchmark-cs.conf index d0d14b8d9..79721be14 100644 --- a/src/benchmark/benchmark-cs.conf +++ b/src/benchmark/benchmark-cs.conf @@ -12,6 +12,7 @@ CURRENCY_ROUND_UNIT = EUR:0.01 [exchange] +AML_THRESHOLD = EUR:99999999 SIGNKEY_LEGAL_DURATION = 2 years # HTTP port the exchange listens to @@ -50,6 +51,7 @@ HTTP_PORT = 8082 SERVE = http MAX_DEBT = EUR:100000000000.0 MAX_DEBT_BANK = EUR:1000000000000000.0 +DATABASE = bank-db.sqlite3 [benchmark] USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42 diff --git a/src/benchmark/benchmark-rsa.conf b/src/benchmark/benchmark-rsa.conf index 7b5b0d1f1..5e44781d1 100644 --- a/src/benchmark/benchmark-rsa.conf +++ b/src/benchmark/benchmark-rsa.conf @@ -12,6 +12,7 @@ CURRENCY_ROUND_UNIT = EUR:0.01 [exchange] +AML_THRESHOLD = EUR:99999999 SIGNKEY_LEGAL_DURATION = 2 years # HTTP port the exchange listens to @@ -50,6 +51,7 @@ HTTP_PORT = 8082 SERVE = http MAX_DEBT = EUR:100000000000.0 MAX_DEBT_BANK = EUR:1000000000000000.0 +DATABASE = bank-db.sqlite3 [benchmark] USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42 diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c index fd7553813..8e61129f0 100644 --- a/src/benchmark/taler-exchange-benchmark.c +++ b/src/benchmark/taler-exchange-benchmark.c @@ -521,7 +521,7 @@ launch_fakebank (void *cls) * @param config_file configuration file to use * @return #GNUNET_OK on success */ -static int +static enum GNUNET_GenericReturnValue parallel_benchmark (TALER_TESTING_Main main_cb, void *main_cb_cls, const char *config_file) @@ -565,7 +565,7 @@ parallel_benchmark (TALER_TESTING_Main main_cb, if (GNUNET_OK != TALER_TESTING_prepare_bank (cfg_filename, GNUNET_NO, - "exchange-account-2", + "exchange-account-test", &bc)) { return 1; @@ -1061,32 +1061,20 @@ main (int argc, } if ( (MODE_EXCHANGE == mode) || (MODE_BOTH == mode) ) { - struct GNUNET_OS_Process *compute_wire_response; - - compute_wire_response - = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL, - NULL, NULL, NULL, - "taler-exchange-wire", - "taler-exchange-wire", - "-c", cfg_filename, - NULL); - if (NULL == compute_wire_response) + /* If we use the fakebank, we MUST reset the database as the fakebank + will have forgotten everything... */ + if (GNUNET_OK != + TALER_TESTING_prepare_exchange (cfg_filename, + (GNUNET_YES == use_fakebank) + ? GNUNET_YES + : GNUNET_NO, + &ec)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to run `taler-exchange-wire`, is your PATH correct?\n"); + "Failed to prepare the exchange for launch\n"); GNUNET_free (cfg_filename); return BAD_CONFIG_FILE; } - GNUNET_OS_process_wait (compute_wire_response); - GNUNET_OS_process_destroy (compute_wire_response); - /* If we use the fakebank, we MUST reset the database as the fakebank - will have forgotten everything... */ - GNUNET_assert (GNUNET_OK == - TALER_TESTING_prepare_exchange (cfg_filename, - (GNUNET_YES == use_fakebank) - ? GNUNET_YES - : GNUNET_NO, - &ec)); } else { diff --git a/src/exchange-tools/taler-exchange-offline.c b/src/exchange-tools/taler-exchange-offline.c index 80765dd9b..b1aababc4 100644 --- a/src/exchange-tools/taler-exchange-offline.c +++ b/src/exchange-tools/taler-exchange-offline.c @@ -3397,14 +3397,28 @@ do_set_global_fee (char *const *args) (NULL == args[3]) || (NULL == args[4]) || (NULL == args[5]) || - (NULL == args[6]) || - ( (1 != sscanf (args[0], - "%u%c", - &year, - &dummy)) && - (0 != strcasecmp ("now", - args[0])) ) || - (GNUNET_OK != + (NULL == args[6]) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n"); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (1 != sscanf (args[0], + "%u%c", + &year, + &dummy)) && + (0 != strcasecmp ("now", + args[0])) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid YEAR given for 'global-fee' subcommand\n"); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (GNUNET_OK != TALER_string_to_amount (args[1], &fees.history)) || (GNUNET_OK != @@ -3412,20 +3426,34 @@ do_set_global_fee (char *const *args) &fees.account)) || (GNUNET_OK != TALER_string_to_amount (args[3], - &fees.purse)) || - (GNUNET_OK != + &fees.purse)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid amount given for 'global-fee' subcommand\n"); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if ( (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (args[4], &purse_timeout)) || (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (args[5], - &history_expiration)) || - (1 != sscanf (args[6], - "%u%c", - &purse_account_limit, - &dummy)) ) + &history_expiration)) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n"); + "Invalid delay given for 'global-fee' subcommand\n"); + test_shutdown (); + global_ret = EXIT_INVALIDARGUMENT; + return; + } + if (1 != sscanf (args[6], + "%u%c", + &purse_account_limit, + &dummy)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid purse account limit given for 'global-fee' subcommand\n"); test_shutdown (); global_ret = EXIT_INVALIDARGUMENT; return; diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 3d87a2a58..ba74a10f5 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -15,6 +15,8 @@ pkgcfg_DATA = \ exchange.conf # Programs +bin_SCRIPTS = \ + taler-exchange-kyc-aml-pep-trigger.sh bin_PROGRAMS = \ taler-exchange-aggregator \ @@ -131,6 +133,7 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_age-withdraw.c taler-exchange-httpd_age-withdraw.h \ taler-exchange-httpd_age-withdraw_reveal.c taler-exchange-httpd_age-withdraw_reveal.h \ taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \ + taler-exchange-httpd_common_kyc.c taler-exchange-httpd_common_kyc.h \ taler-exchange-httpd_config.c taler-exchange-httpd_config.h \ taler-exchange-httpd_contract.c taler-exchange-httpd_contract.h \ taler-exchange-httpd_csr.c taler-exchange-httpd_csr.h \ @@ -227,4 +230,5 @@ EXTRA_DIST = \ test_taler_exchange_httpd.get \ test_taler_exchange_httpd.post \ exchange.conf \ + $(bin_SCRIPTS) \ $(check_SCRIPTS) diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf index 38e5816ff..d1558a49c 100644 --- a/src/exchange/exchange.conf +++ b/src/exchange/exchange.conf @@ -6,10 +6,23 @@ # This must be adjusted to your actual installation. # MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG +# Must be set to the threshold above which transactions +# are flagged for AML review. +# AML_THRESHOLD = + +# Specifies a program (binary) to run on KYC attribute data to decide +# whether we should immediately flag an account for AML review. +# The KYC attribute data will be passed on standard-input. +# Return non-zero to trigger AML review of the new user. +KYC_AML_TRIGGER = true + # Attribute encryption key for storing attributes encrypted # in the database. Should be a high-entropy nonce. ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE +# Set to NO to disable tipping. +ENABLE_TIPPING = YES + # How long do we allow /keys to be cached at most? The actual # limit is the minimum of this value and the first expected # significant change in /keys based on the expiration times. diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c index 38110a5e7..0073d85ec 100644 --- a/src/exchange/taler-exchange-aggregator.c +++ b/src/exchange/taler-exchange-aggregator.c @@ -303,17 +303,17 @@ parse_aggregator_config (void) (TALER_amount_is_zero (¤cy_round_unit)) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Need non-zero amount in section `TALER' under `CURRENCY_ROUND_UNIT'\n"); + "Need non-zero amount in section `taler' under `CURRENCY_ROUND_UNIT'\n"); return GNUNET_SYSERR; } if (GNUNET_OK != TALER_config_get_amount (cfg, - "taler", + "exchange", "AML_THRESHOLD", &aml_threshold)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Need amount in section `TALER' under `AML_THRESHOLD'\n"); + "Need amount in section `exchange' under `AML_THRESHOLD'\n"); return GNUNET_SYSERR; } diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 7e11655c1..348967f77 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -153,6 +153,16 @@ struct TALER_EXCHANGEDB_Plugin *TEH_plugin; */ char *TEH_currency; +/** + * Name of the KYC-AML-trigger evaluation binary. + */ +char *TEH_kyc_aml_trigger; + +/** + * Option set to #GNUNET_YES if tipping is enabled. + */ +int TEH_enable_tipping; + /** * What is the largest amount we allow a peer to * merge into a reserve before always triggering @@ -1844,6 +1854,17 @@ exchange_serve_process_config (void) "valid relative time expected"); return GNUNET_SYSERR; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (TEH_cfg, + "exchange", + "KYC_AML_TRIGGER", + &TEH_kyc_aml_trigger)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "exchange", + "KYC_AML_TRIGGER"); + return GNUNET_SYSERR; + } if (GNUNET_OK != TALER_config_get_currency (TEH_cfg, &TEH_currency)) @@ -1855,19 +1876,30 @@ exchange_serve_process_config (void) } if (GNUNET_OK != TALER_config_get_amount (TEH_cfg, - "taler", + "exchange", "AML_THRESHOLD", &TEH_aml_threshold)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Need amount in section `TALER' under `AML_THRESHOLD'\n"); + "Need amount in section `exchange' under `AML_THRESHOLD'\n"); return GNUNET_SYSERR; } if (0 != strcmp (TEH_currency, TEH_aml_threshold.currency)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Amount in section `TALER' under `AML_THRESHOLD' uses the wrong currency!\n"); + "Amount in section `exchange' under `AML_THRESHOLD' uses the wrong currency!\n"); + return GNUNET_SYSERR; + } + TEH_enable_tipping + = GNUNET_CONFIGURATION_get_value_yesno ( + TEH_cfg, + "exchange", + "ENABLE_TIPPING"); + if (GNUNET_SYSERR == TEH_enable_tipping) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Need YES or NO in section `exchange' under `ENABLE_TIPPING'\n"); return GNUNET_SYSERR; } if (GNUNET_OK != diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h index 5ab0ea92b..24e087721 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -64,6 +64,11 @@ extern int TEH_check_invariants_flag; */ extern int TEH_allow_keys_timetravel; +/** + * Option set to #GNUNET_YES if tipping is enabled. + */ +extern int TEH_enable_tipping; + /** * Main directory with revocation data. */ @@ -97,6 +102,11 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin; */ extern char *TEH_currency; +/** + * Name of the KYC-AML-trigger evaluation binary. + */ +extern char *TEH_kyc_aml_trigger; + /** * What is the largest amount we allow a peer to * merge into a reserve before always triggering diff --git a/src/exchange/taler-exchange-httpd_aml-decision-get.c b/src/exchange/taler-exchange-httpd_aml-decision-get.c index 6b36fe27f..b4f337db1 100644 --- a/src/exchange/taler-exchange-httpd_aml-decision-get.c +++ b/src/exchange/taler-exchange-httpd_aml-decision-get.c @@ -43,8 +43,6 @@ * @param[in,out] cls closure with a `json_t *` array to update * @param h_payto account for which the attribute data is stored * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes @@ -55,7 +53,6 @@ kyc_attribute_cb ( void *cls, const struct TALER_PaytoHashP *h_payto, const char *provider_section, - const char *birthdate, struct GNUNET_TIME_Timestamp collection_time, struct GNUNET_TIME_Timestamp expiration_time, size_t enc_attributes_size, diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c new file mode 100644 index 000000000..ef917a559 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_common_kyc.c @@ -0,0 +1,239 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_common_kyc.c + * @brief shared logic for finishing a KYC process + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-exchange-httpd_common_kyc.h" +#include "taler_attributes.h" +#include "taler_exchangedb_plugin.h" + +struct TEH_KycAmlTrigger +{ + + /** + * Our logging scope. + */ + struct GNUNET_AsyncScopeId scope; + + /** + * account the operation is about + */ + struct TALER_PaytoHashP account_id; + + /** + * until when is the KYC data valid + */ + struct GNUNET_TIME_Absolute expiration; + + /** + * legitimization process the KYC data is about + */ + uint64_t process_row; + + /** + * name of the configuration section of the logic that was run + */ + char *provider_section; + + /** + * set to user ID at the provider, or NULL if not supported or unknown + */ + char *provider_user_id; + + /** + * provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown + */ + char *provider_legitimization_id; + + /** + * function to call with the result + */ + TEH_KycAmlTriggerCallback cb; + + /** + * closure for @e cb + */ + void *cb_cls; + + /** + * user attributes returned by the provider + */ + json_t *attributes; + + /** + * response to return to the HTTP client + */ + struct MHD_Response *response; + + /** + * Handle to an external process that evaluates the + * need to run AML on the account. + */ + struct TALER_JSON_ExternalConversion *kyc_aml; + + /** + * HTTP status code of @e response + */ + unsigned int http_status; + +}; + + +/** + * Type of a callback that receives a JSON @a result. + * + * @param cls closure of type `struct TEH_KycAmlTrigger *` + * @param status_type how did the process die + * @param code termination status code from the process + * @param result some JSON result, NULL if we failed to get an JSON output + */ +static void +kyc_aml_finished (void *cls, + enum GNUNET_OS_ProcessStatusType status_type, + unsigned long code, + const json_t *result) +{ + struct TEH_KycAmlTrigger *kat = cls; + enum GNUNET_DB_QueryStatus qs; + size_t eas; + void *ea; + const char *birthdate; + unsigned int birthday; + struct GNUNET_ShortHashCode kyc_prox; + struct GNUNET_AsyncScopeSave old_scope; + + kat->kyc_aml = NULL; + GNUNET_async_scope_enter (&kat->scope, + &old_scope); + TALER_CRYPTO_attributes_to_kyc_prox (kat->attributes, + &kyc_prox); + birthdate = json_string_value (json_object_get (kat->attributes, + TALER_ATTRIBUTE_BIRTHDATE)); + birthday = 0; (void) birthdate; // FIXME-Oec: calculate birthday here... + // Convert 'birthdate' to time after 1970, then compute days. + // Then compare against max age-restriction, and if before, set to 0. + TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, + kat->attributes, + &ea, + &eas); + qs = TEH_plugin->insert_kyc_attributes ( + TEH_plugin->cls, + kat->process_row, + &kat->account_id, + &kyc_prox, + kat->provider_section, + birthday, + GNUNET_TIME_timestamp_get (), + kat->provider_user_id, + kat->provider_legitimization_id, + kat->expiration, + eas, + ea, + 0 != code); + GNUNET_free (ea); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + GNUNET_break (0); + if (NULL != kat->response) + MHD_destroy_response (kat->response); + kat->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + kat->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, + "do_insert_kyc_attributes"); + } + /* Finally, return result to main handler */ + kat->cb (kat->cb_cls, + kat->http_status, + kat->response); + kat->response = NULL; + TEH_kyc_finished_cancel (kat); + GNUNET_async_scope_restore (&old_scope); +} + + +struct TEH_KycAmlTrigger * +TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope, + uint64_t process_row, + const struct TALER_PaytoHashP *account_id, + const char *provider_section, + const char *provider_user_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration, + const json_t *attributes, + unsigned int http_status, + struct MHD_Response *response, + TEH_KycAmlTriggerCallback cb, + void *cb_cls) +{ + struct TEH_KycAmlTrigger *kat; + + kat = GNUNET_new (struct TEH_KycAmlTrigger); + kat->scope = *scope; + kat->process_row = process_row; + kat->account_id = *account_id; + kat->provider_section + = GNUNET_strdup (provider_section); + if (NULL != provider_user_id) + kat->provider_user_id + = GNUNET_strdup (provider_user_id); + if (NULL != provider_legitimization_id) + kat->provider_legitimization_id + = GNUNET_strdup (provider_legitimization_id); + kat->expiration = expiration; + kat->attributes = json_incref ((json_t*) attributes); + kat->http_status = http_status; + kat->response = response; + kat->cb = cb; + kat->cb_cls = cb_cls; + kat->kyc_aml + = TALER_JSON_external_conversion_start ( + attributes, + &kyc_aml_finished, + kat, + TEH_kyc_aml_trigger, + TEH_kyc_aml_trigger, + NULL); + if (NULL == kat->kyc_aml) + { + GNUNET_break (0); + TEH_kyc_finished_cancel (kat); + return NULL; + } + return kat; +} + + +void +TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat) +{ + if (NULL != kat->kyc_aml) + { + TALER_JSON_external_conversion_stop (kat->kyc_aml); + kat->kyc_aml = NULL; + } + GNUNET_free (kat->provider_section); + GNUNET_free (kat->provider_user_id); + GNUNET_free (kat->provider_legitimization_id); + json_decref (kat->attributes); + if (NULL != kat->response) + { + MHD_destroy_response (kat->response); + kat->response = NULL; + } + GNUNET_free (kat); +} diff --git a/src/exchange/taler-exchange-httpd_common_kyc.h b/src/exchange/taler-exchange-httpd_common_kyc.h new file mode 100644 index 000000000..572766041 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_common_kyc.h @@ -0,0 +1,99 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_common_kyc.h + * @brief shared logic for finishing a KYC process + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_COMMON_KYC_H +#define TALER_EXCHANGE_HTTPD_COMMON_KYC_H + +#include +#include +#include +#include +#include "taler_json_lib.h" +#include "taler_mhd_lib.h" +#include "taler-exchange-httpd.h" + + +/** + * Function called after the KYC-AML trigger is done. + * + * @param cls closure + * @param http_status final HTTP status to return + * @param[in] response final HTTP ro return + */ +typedef void +(*TEH_KycAmlTriggerCallback) ( + void *cls, + unsigned int http_status, + struct MHD_Response *response); + + +/** + * Handle for an asynchronous operation to finish + * a KYC process after running the AML trigger. + */ +struct TEH_KycAmlTrigger; + +// FIXME: also pass async log context and set it! +/** + * We have finished a KYC process and obtained new + * @a attributes for a given @a account_id. + * Check with the KYC-AML trigger to see if we need + * to initiate an AML process, and store the attributes + * in the database. Then call @a cb. + * + * @param scope the HTTP request logging scope + * @param process_row legitimization process the webhook was about + * @param account_id account the webhook was about + * @param provider_section name of the configuration section of the logic that was run + * @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown + * @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown + * @param expiration until when is the KYC check valid + * @param attributes user attributes returned by the provider + * @param http_status HTTP status code of @a response + * @param[in] response to return to the HTTP client + * @param cb function to call with the result + * @param cb_cls closure for @a cb + * @return handle to cancel the operation + */ +struct TEH_KycAmlTrigger * +TEH_kyc_finished (const struct GNUNET_AsyncScopeId *scope, + uint64_t process_row, + const struct TALER_PaytoHashP *account_id, + const char *provider_section, + const char *provider_user_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration, + const json_t *attributes, + unsigned int http_status, + struct MHD_Response *response, + TEH_KycAmlTriggerCallback cb, + void *cb_cls); + + +/** + * Cancel KYC finish operation. + * + * @param[in] kat operation to abort + */ +void +TEH_kyc_finished_cancel (struct TEH_KycAmlTrigger *kat); + + +#endif diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index fa87a3b84..b39093ec1 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -1857,6 +1857,8 @@ create_krd (struct TEH_KeyStateHandle *ksh, TEH_currency), GNUNET_JSON_pack_string ("asset_type", asset_type), + GNUNET_JSON_pack_bool ("tipping_allowed", + GNUNET_YES == TEH_enable_tipping), GNUNET_JSON_pack_data_auto ("master_public_key", &TEH_master_public_key), GNUNET_JSON_pack_time_rel ("reserve_closing_delay", diff --git a/src/exchange/taler-exchange-httpd_kyc-proof.c b/src/exchange/taler-exchange-httpd_kyc-proof.c index 9668ee54c..b3175bd28 100644 --- a/src/exchange/taler-exchange-httpd_kyc-proof.c +++ b/src/exchange/taler-exchange-httpd_kyc-proof.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021-2022 Taler Systems SA + Copyright (C) 2021-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -23,11 +23,11 @@ #include #include #include -#include #include "taler_attributes.h" #include "taler_json_lib.h" #include "taler_kyclogic_lib.h" #include "taler_mhd_lib.h" +#include "taler-exchange-httpd_common_kyc.h" #include "taler-exchange-httpd_kyc-proof.h" #include "taler-exchange-httpd_responses.h" @@ -68,6 +68,11 @@ struct KycProofContext */ struct TALER_KYCLOGIC_ProofHandle *ph; + /** + * KYC AML trigger operation. + */ + struct TEH_KycAmlTrigger *kat; + /** * Process information about the user for the plugin from the database, can * be NULL. @@ -159,6 +164,28 @@ TEH_kyc_proof_cleanup (void) } +/** + * Function called after the KYC-AML trigger is done. + * + * @param cls closure + * @param http_status final HTTP status to return + * @param[in] response final HTTP ro return + */ +static void +proof_finish ( + void *cls, + unsigned int http_status, + struct MHD_Response *response) +{ + struct KycProofContext *kpc = cls; + + kpc->kat = NULL; + kpc->response_code = http_status; + kpc->response = response; + kpc_resume (kpc); +} + + /** * Function called with the result of a proof check operation. * @@ -192,74 +219,40 @@ proof_cb ( kpc->ph = NULL; GNUNET_async_scope_enter (&rc->async_scope_id, &old_scope); - if (TALER_KYCLOGIC_STATUS_SUCCESS == status) { - enum GNUNET_DB_QueryStatus qs; - size_t eas; - void *ea; - const char *birthdate; - struct GNUNET_ShortHashCode kyc_prox; - - TALER_CRYPTO_attributes_to_kyc_prox (attributes, - &kyc_prox); - birthdate = json_string_value (json_object_get (attributes, - TALER_ATTRIBUTE_BIRTHDATE)); - TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, - attributes, - &ea, - &eas); - qs = TEH_plugin->insert_kyc_attributes ( - TEH_plugin->cls, - &kpc->h_payto, - &kyc_prox, - kpc->provider_section, - birthdate, - GNUNET_TIME_timestamp_get (), - GNUNET_TIME_absolute_to_timestamp (expiration), - eas, - ea); - GNUNET_free (ea); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) + kpc->kat = TEH_kyc_finished (&rc->async_scope_id, + kpc->process_row, + &kpc->h_payto, + kpc->provider_section, + provider_user_id, + provider_legitimization_id, + expiration, + attributes, + http_status, + response, + &proof_finish, + kpc); + if (NULL == kpc->kat) { - GNUNET_break (0); + http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; if (NULL != response) MHD_destroy_response (response); - kpc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - kpc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_attributes"); - GNUNET_async_scope_restore (&old_scope); - return; - } - qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls, - kpc->process_row, - kpc->provider_section, - &kpc->h_payto, - provider_user_id, - provider_legitimization_id, - expiration); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - { - GNUNET_break (0); - if (NULL != response) - MHD_destroy_response (response); - kpc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - kpc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, - "set_kyc_ok"); - GNUNET_async_scope_restore (&old_scope); - return; + response = TALER_MHD_make_error ( + TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, + "[exchange] AML_KYC_TRIGGER"); } } - else + if (NULL == kpc->kat) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "KYC process #%llu failed with status %d\n", (unsigned long long) kpc->process_row, status); + proof_finish (kpc, + http_status, + response); } - kpc->response_code = http_status; - kpc->response = response; - kpc_resume (kpc); GNUNET_async_scope_restore (&old_scope); } @@ -279,6 +272,11 @@ clean_kpc (struct TEH_RequestContext *rc) kpc->logic->proof_cancel (kpc->ph); kpc->ph = NULL; } + if (NULL != kpc->kat) + { + TEH_kyc_finished_cancel (kpc->kat); + kpc->kat = NULL; + } if (NULL != kpc->response) { MHD_destroy_response (kpc->response); diff --git a/src/exchange/taler-exchange-httpd_kyc-webhook.c b/src/exchange/taler-exchange-httpd_kyc-webhook.c index f8fe711da..415e5de9a 100644 --- a/src/exchange/taler-exchange-httpd_kyc-webhook.c +++ b/src/exchange/taler-exchange-httpd_kyc-webhook.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -28,6 +28,7 @@ #include "taler_json_lib.h" #include "taler_mhd_lib.h" #include "taler_kyclogic_lib.h" +#include "taler-exchange-httpd_common_kyc.h" #include "taler-exchange-httpd_kyc-webhook.h" #include "taler-exchange-httpd_responses.h" @@ -53,6 +54,11 @@ struct KycWebhookContext */ struct TEH_RequestContext *rc; + /** + * Handle for the KYC-AML trigger interaction. + */ + struct TEH_KycAmlTrigger *kat; + /** * Plugin responsible for the webhook. */ @@ -140,6 +146,28 @@ TEH_kyc_webhook_cleanup (void) } +/** + * Function called after the KYC-AML trigger is done. + * + * @param cls closure with a `struct KycWebhookContext *` + * @param http_status final HTTP status to return + * @param[in] response final HTTP ro return + */ +static void +kyc_aml_webhook_finished ( + void *cls, + unsigned int http_status, + struct MHD_Response *response) +{ + struct KycWebhookContext *kwh = cls; + + kwh->kat = NULL; + kwh->response = response; + kwh->response_code = http_status; + kwh_resume (kwh); +} + + /** * Function called with the result of a KYC webhook operation. * @@ -178,58 +206,27 @@ webhook_finished_cb ( switch (status) { case TALER_KYCLOGIC_STATUS_SUCCESS: - /* _successfully_ resumed case */ + kwh->kat = TEH_kyc_finished ( + &kwh->rc->async_scope_id, + process_row, + account_id, + provider_section, + provider_user_id, + provider_legitimization_id, + expiration, + attributes, + http_status, + response, + &kyc_aml_webhook_finished, + kwh); + if (NULL == kwh->kat) { - enum GNUNET_DB_QueryStatus qs; - size_t eas; - void *ea; - const char *birthdate; - struct GNUNET_ShortHashCode kyc_prox; - - TALER_CRYPTO_attributes_to_kyc_prox (attributes, - &kyc_prox); - birthdate = json_string_value (json_object_get (attributes, - TALER_ATTRIBUTE_BIRTHDATE)); - TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key, - attributes, - &ea, - &eas); - qs = TEH_plugin->insert_kyc_attributes ( - TEH_plugin->cls, - account_id, - &kyc_prox, - provider_section, - birthdate, - GNUNET_TIME_timestamp_get (), - GNUNET_TIME_absolute_to_timestamp (expiration), - eas, - ea); - GNUNET_free (ea); - if (qs < 0) - { - GNUNET_break (0); - kwh->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_kyc_attributes"); - kwh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - kwh_resume (kwh); - return; - } - qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls, - process_row, - provider_section, - account_id, - provider_user_id, - provider_legitimization_id, - expiration); - if (qs < 0) - { - GNUNET_break (0); - kwh->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED, - "set_kyc_ok"); - kwh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - kwh_resume (kwh); - return; - } + http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + if (NULL != response) + MHD_destroy_response (response); + response = TALER_MHD_make_error ( + TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION, + "[exchange] AML_KYC_TRIGGER"); } break; default: @@ -241,9 +238,10 @@ webhook_finished_cb ( status); break; } - kwh->response = response; - kwh->response_code = http_status; - kwh_resume (kwh); + if (NULL == kwh->kat) + kyc_aml_webhook_finished (kwh, + http_status, + response); } @@ -262,6 +260,11 @@ clean_kwh (struct TEH_RequestContext *rc) kwh->plugin->webhook_cancel (kwh->wh); kwh->wh = NULL; } + if (NULL != kwh->kat) + { + TEH_kyc_finished_cancel (kwh->kat); + kwh->kat = NULL; + } if (NULL != kwh->response) { MHD_destroy_response (kwh->response); diff --git a/src/exchange/taler-exchange-httpd_reserves_attest.c b/src/exchange/taler-exchange-httpd_reserves_attest.c index 297d8ceec..d0f3614e6 100644 --- a/src/exchange/taler-exchange-httpd_reserves_attest.c +++ b/src/exchange/taler-exchange-httpd_reserves_attest.c @@ -158,8 +158,6 @@ reply_reserve_attest_success (struct MHD_Connection *connection, * @param cls our `struct ReserveAttestContext *` * @param h_payto account for which the attribute data is stored * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes @@ -169,7 +167,6 @@ static void kyc_process_cb (void *cls, const struct TALER_PaytoHashP *h_payto, const char *provider_section, - const char *birthdate, struct GNUNET_TIME_Timestamp collection_time, struct GNUNET_TIME_Timestamp expiration_time, size_t enc_attributes_size, diff --git a/src/exchange/taler-exchange-httpd_reserves_get_attest.c b/src/exchange/taler-exchange-httpd_reserves_get_attest.c index b53a8641a..ae983682a 100644 --- a/src/exchange/taler-exchange-httpd_reserves_get_attest.c +++ b/src/exchange/taler-exchange-httpd_reserves_get_attest.c @@ -64,8 +64,6 @@ struct ReserveAttestContext * @param cls our `struct ReserveAttestContext *` * @param h_payto account for which the attribute data is stored * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes @@ -75,7 +73,6 @@ static void kyc_process_cb (void *cls, const struct TALER_PaytoHashP *h_payto, const char *provider_section, - const char *birthdate, struct GNUNET_TIME_Timestamp collection_time, struct GNUNET_TIME_Timestamp expiration_time, size_t enc_attributes_size, diff --git a/src/exchange/taler-exchange-kyc-aml-pep-trigger.sh b/src/exchange/taler-exchange-kyc-aml-pep-trigger.sh new file mode 100755 index 000000000..9baa32baf --- /dev/null +++ b/src/exchange/taler-exchange-kyc-aml-pep-trigger.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# This file is in the public domain. +# This is an example of how to trigger AML if the +# KYC attributes include '{"pep":true}' +# +# To be used as a script for the KYC_AML_TRIGGER. +test "false" = $(jq .pep -) diff --git a/src/exchange/test_taler_exchange_httpd.conf b/src/exchange/test_taler_exchange_httpd.conf index 0af23b9df..80cf62308 100644 --- a/src/exchange/test_taler_exchange_httpd.conf +++ b/src/exchange/test_taler_exchange_httpd.conf @@ -7,13 +7,14 @@ TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/ # Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 [auditor] TINY_AMOUNT = EUR:0.01 [exchange] +AML_THRESHOLD = EUR:1000000 + # Directory with our terms of service. TERMS_DIR = ../../contrib/tos diff --git a/src/exchangedb/0002-reserves.sql b/src/exchangedb/0002-reserves.sql index 03d17aee2..df5b6c3db 100644 --- a/src/exchangedb/0002-reserves.sql +++ b/src/exchangedb/0002-reserves.sql @@ -31,7 +31,7 @@ BEGIN ',current_balance_frac INT4 NOT NULL DEFAULT(0)' ',purses_active INT8 NOT NULL DEFAULT(0)' ',purses_allowed INT8 NOT NULL DEFAULT(0)' - ',max_age INT4 NOT NULL DEFAULT(120)' + ',max_age INT4 NOT NULL DEFAULT(0)' ',expiration_date INT8 NOT NULL' ',gc_date INT8 NOT NULL' ') %s ;' @@ -80,6 +80,12 @@ BEGIN ,table_name ,partition_suffix ); + PERFORM comment_partitioned_column( + 'Birthday of the user in days after 1970, or 0 if user is an adult and is not subject to age restrictions' + ,'max_age' + ,table_name + ,partition_suffix + ); END $$; diff --git a/src/exchangedb/0004-kyc_attributes.sql b/src/exchangedb/0004-kyc_attributes.sql new file mode 100644 index 000000000..e45d46b3b --- /dev/null +++ b/src/exchangedb/0004-kyc_attributes.sql @@ -0,0 +1,44 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2023 Taler Systems SA +-- +-- TALER is free software; you can redistribute it and/or modify it under the +-- terms of the GNU General Public License as published by the Free Software +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see +-- + +CREATE OR REPLACE FUNCTION master_table_kyc_attributes_V2() +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name VARCHAR DEFAULT 'kyc_attributes'; +BEGIN + EXECUTE FORMAT ( + 'ALTER TABLE ' || table_name || + ' DROP COLUMN birthdate;' + ); +END $$; + +COMMENT ON FUNCTION master_table_kyc_attributes_V2 + IS 'Removes birthdate column from the kyc_attributes table'; + +INSERT INTO exchange_tables + (name + ,version + ,action + ,partitioned + ,by_range) + VALUES + ('kyc_attributes_V2' + ,'exchange-0004' + ,'master' + ,TRUE + ,FALSE); diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index ee78b87f7..4df9cb406 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -144,7 +144,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_aggregate.h pg_aggregate.c \ pg_create_aggregation_transient.h pg_create_aggregation_transient.c \ pg_insert_kyc_attributes.h pg_insert_kyc_attributes.c \ - pg_update_kyc_attributes.h pg_update_kyc_attributes.c \ pg_select_similar_kyc_attributes.h pg_select_similar_kyc_attributes.c \ pg_select_kyc_attributes.h pg_select_kyc_attributes.c \ pg_insert_aml_officer.h pg_insert_aml_officer.c \ diff --git a/src/exchangedb/exchange-0004.sql.in b/src/exchangedb/exchange-0004.sql.in index 00979e193..02bdf017a 100644 --- a/src/exchangedb/exchange-0004.sql.in +++ b/src/exchangedb/exchange-0004.sql.in @@ -1,6 +1,6 @@ -- -- This file is part of TALER --- Copyright (C) 2014--2023 Taler Systems SA +-- Copyright (C) 2023 Taler Systems SA -- -- TALER is free software; you can redistribute it and/or modify it under the -- terms of the GNU General Public License as published by the Free Software @@ -19,6 +19,7 @@ BEGIN; SELECT _v.register_patch('exchange-0004', NULL, NULL); SET search_path TO exchange; +#include "0004-kyc_attributes.sql" #include "0004-wire_accounts.sql" COMMIT; diff --git a/src/exchangedb/exchange_do_insert_kyc_attributes.sql b/src/exchangedb/exchange_do_insert_kyc_attributes.sql new file mode 100644 index 000000000..f1959a66e --- /dev/null +++ b/src/exchangedb/exchange_do_insert_kyc_attributes.sql @@ -0,0 +1,92 @@ +-- +-- This file is part of TALER +-- Copyright (C) 2023 Taler Systems SA +-- +-- TALER is free software; you can redistribute it and/or modify it under the +-- terms of the GNU General Public License as published by the Free Software +-- Foundation; either version 3, or (at your option) any later version. +-- +-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY +-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +-- A PARTICULAR PURPOSE. See the GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License along with +-- TALER; see the file COPYING. If not, see +-- + +CREATE OR REPLACE FUNCTION exchange_do_insert_kyc_attributes( + IN in_process_row INT8, + IN in_h_payto BYTEA, + IN in_kyc_prox BYTEA, + IN in_provider_section VARCHAR, + IN in_birthday INT4, + IN in_provider_account_id VARCHAR, + IN in_provider_legitimization_id VARCHAR, + IN in_collection_time_ts INT8, + IN in_expiration_time INT8, + IN in_expiration_time_ts INT8, + IN in_enc_attributes BYTEA, + IN in_require_aml BOOLEAN, + IN in_kyc_completed_notify_s VARCHAR, + OUT out_ok BOOLEAN) +LANGUAGE plpgsql +AS $$ +BEGIN + +INSERT INTO exchange.kyc_attributes + (h_payto + ,kyc_prox + ,provider + ,collection_time + ,expiration_time + ,encrypted_attributes + ) VALUES + (in_h_payto + ,in_kyc_prox + ,in_provider_section + ,in_collection_time_ts + ,in_expiration_time_ts + ,in_enc_attributes); + +-- FIXME-Oec: modify to 'return' the reserve_pub here +-- (requires of course to modify other code to store +-- the reserve pub in the right table in the first place) +UPDATE exchange.legitimization_processes + SET provider_user_id=in_provider_account_id + ,provider_legitimization_id=in_provider_legitimization_id + ,expiration_time=GREATEST(expiration_time,in_expiration_time) + WHERE h_payto=in_h_payto + AND legitimization_process_serial_id=in_process_row + AND provider_section=in_provider_section; +out_ok = FOUND; + +-- FIXME-Oec: update exchange reserve table to store in_birthday here! +-- UPDATE exchange.reserves SET max_age=in_birthday WHERE reserve_pub=X; + +IF in_require_aml +THEN + INSERT INTO exchange.aml_status + (h_payto + ,status) + VALUES + (in_h_payto + ,1) + ON CONFLICT (h_payto) DO + UPDATE SET status=EXCLUDED.status | 1; +END IF; + +-- Wake up everyone who might care... +PERFORM pg_notify (in_kyc_completed_notify_s, NULL); + +INSERT INTO kyc_alerts + (h_payto + ,trigger_type) + VALUES + (in_h_payto,1); + + +END $$; + + +COMMENT ON FUNCTION exchange_do_insert_kyc_attributes(INT8, BYTEA, BYTEA, VARCHAR, INT4, VARCHAR, VARCHAR, INT8, INT8, INT8, BYTEA, BOOL, VARCHAR) + IS 'Inserts new KYC attributes and updates the status of the legitimization process and the AML status for the account'; diff --git a/src/exchangedb/exchange_do_reserves_in_insert.sql b/src/exchangedb/exchange_do_reserves_in_insert.sql index dffcd8b55..cf57d8e81 100644 --- a/src/exchangedb/exchange_do_reserves_in_insert.sql +++ b/src/exchangedb/exchange_do_reserves_in_insert.sql @@ -963,3 +963,139 @@ BEGIN CLOSE curs_transaction_exist; RETURN; END $$; + + + + + + + +DO $$ +BEGIN + CREATE TYPE exchange_do_array_reserve_insert_return_type + AS + (transaction_duplicate BOOLEAN + ,ruuid INT8); +EXCEPTION + WHEN duplicate_object THEN null; +END +$$; + +CREATE OR REPLACE FUNCTION exchange_do_array_reserves_insert( + IN in_gc_date INT8, + IN in_reserve_expiration INT8, + IN ina_reserve_pub BYTEA[], + IN ina_wire_ref INT8[], + IN ina_credit_val INT8[], + IN ina_credit_frac INT4[], + IN ina_exchange_account_name VARCHAR[], + IN ina_execution_date INT8[], + IN ina_wire_source_h_payto BYTEA[], + IN ina_payto_uri VARCHAR[], + IN ina_notify TEXT[]) +RETURNS SETOF exchange_do_array_reserve_insert_return_type +LANGUAGE plpgsql +AS $$ +DECLARE + curs REFCURSOR; +DECLARE + conflict BOOL; +DECLARE + dup BOOL; +DECLARE + uuid INT8; +DECLARE + i RECORD; +BEGIN + + INSERT INTO wire_targets + (wire_target_h_payto + ,payto_uri) + SELECT + wire_source_h_payto + ,payto_uri + FROM + UNNEST (ina_wire_source_h_payto) AS wire_source_h_payto + ,UNNEST (ina_payto_uri) AS payto_uri + ON CONFLICT DO NOTHING; + + FOR i IN + SELECT + reserve_pub + ,wire_ref + ,credit_val + ,credit_frac + ,exchange_account_name + ,execution_date + ,wire_source_h_payto + ,payto_uri + ,notify + FROM + UNNEST (ina_reserve_pub) AS reserve_pub + ,UNNEST (ina_wire_ref) AS wire_ref + ,UNNEST (ina_credit_val) AS credit_val + ,UNNEST (ina_credit_frac) AS credit_frac + ,UNNEST (ina_exchange_account_name) AS exchange_account_name + ,UNNEST (ina_execution_date) AS execution_date + ,UNNEST (ina_wire_source_h_payto) AS wire_source_h_payto + ,UNNEST (ina_notify) AS notify + LOOP + INSERT INTO reserves + (reserve_pub + ,current_balance_val + ,current_balance_frac + ,expiration_date + ,gc_date + ) VALUES ( + i.reserve_pub + ,i.credit_val + ,i.credit_frac + ,in_reserve_expiration + ,in_gc_date + ) + ON CONFLICT DO NOTHING + RETURNING reserve_uuid + INTO uuid; + conflict = NOT FOUND; + + INSERT INTO reserves_in + (reserve_pub + ,wire_reference + ,credit_val + ,credit_frac + ,exchange_account_section + ,wire_source_h_payto + ,execution_date + ) VALUES ( + i.reserve_pub + ,i.wire_reference + ,i.credit_val + ,i.credit_frac + ,i.exchange_account_section + ,i.wire_source_h_payto + ,i.execution_date + ) + ON CONFLICT DO NOTHING; + + IF NOT FOUND + THEN + IF conflict + THEN + dup = TRUE; + else + dup = FALSE; + END IF; + ELSE + IF NOT conflict + THEN + EXECUTE FORMAT ( + 'NOTIFY %s' + ,i.notify); + END IF; + dup = FALSE; + END IF; + RETURN NEXT (dup,uuid); + END LOOP; + + RETURN; +END $$; diff --git a/src/exchangedb/pg_insert_kyc_attributes.c b/src/exchangedb/pg_insert_kyc_attributes.c index fd90950fd..361f491e8 100644 --- a/src/exchangedb/pg_insert_kyc_attributes.c +++ b/src/exchangedb/pg_insert_kyc_attributes.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022, 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -29,43 +29,72 @@ enum GNUNET_DB_QueryStatus TEH_PG_insert_kyc_attributes ( void *cls, + uint64_t process_row, const struct TALER_PaytoHashP *h_payto, const struct GNUNET_ShortHashCode *kyc_prox, const char *provider_section, - const char *birthdate, + uint32_t birthday, struct GNUNET_TIME_Timestamp collection_time, - struct GNUNET_TIME_Timestamp expiration_time, + const char *provider_account_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration_time, size_t enc_attributes_size, - const void *enc_attributes) + const void *enc_attributes, + bool require_aml) { struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Timestamp expiration + = GNUNET_TIME_absolute_to_timestamp (expiration_time); + struct TALER_KycCompletedEventP rep = { + .header.size = htons (sizeof (rep)), + .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED), + .h_payto = *h_payto + }; + char *kyc_completed_notify_s + = GNUNET_PG_get_event_notify_channel (&rep.header); struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&process_row), GNUNET_PQ_query_param_auto_from_type (h_payto), GNUNET_PQ_query_param_auto_from_type (kyc_prox), GNUNET_PQ_query_param_string (provider_section), - (NULL == birthdate) + GNUNET_PQ_query_param_uint32 (&birthday), + (NULL == provider_account_id) ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (birthdate), + : GNUNET_PQ_query_param_string (provider_account_id), + (NULL == provider_legitimization_id) + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string (provider_legitimization_id), GNUNET_PQ_query_param_timestamp (&collection_time), - GNUNET_PQ_query_param_timestamp (&expiration_time), + GNUNET_PQ_query_param_absolute_time (&expiration_time), + GNUNET_PQ_query_param_timestamp (&expiration), GNUNET_PQ_query_param_fixed_size (enc_attributes, enc_attributes_size), + GNUNET_PQ_query_param_bool (require_aml), + GNUNET_PQ_query_param_string (kyc_completed_notify_s), GNUNET_PQ_query_param_end }; + bool ok; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("out_ok", + &ok), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; PREPARE (pg, "insert_kyc_attributes", - "INSERT INTO kyc_attributes " - "(h_payto" - ",kyc_prox" - ",provider" - ",birthdate" - ",collection_time" - ",expiration_time" - ",encrypted_attributes" - ") VALUES " - "($1, $2, $3, $4, $5, $6, $7);"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_kyc_attributes", - params); + "SELECT " + " out_ok" + " FROM exchange_do_insert_kyc_attributes " + "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);"); + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "insert_kyc_attributes", + params, + rs); + GNUNET_free (kyc_completed_notify_s); + if (qs < 0) + return qs; + if (! ok) + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + return qs; } diff --git a/src/exchangedb/pg_insert_kyc_attributes.h b/src/exchangedb/pg_insert_kyc_attributes.h index 8ee307d7d..c1aad0eb5 100644 --- a/src/exchangedb/pg_insert_kyc_attributes.h +++ b/src/exchangedb/pg_insert_kyc_attributes.h @@ -27,30 +27,39 @@ /** - * Store KYC attribute data. + * Store KYC attribute data, update KYC process status and + * AML status for the given account. * * @param cls closure + * @param process_row KYC process row to update * @param h_payto account for which the attribute data is stored * @param kyc_prox key for similarity search * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown + * @param provider_account_id provider account ID + * @param provider_legitimization_id provider legitimization ID + * @param birthday birthdate of user, in days after 1990, or 0 if unknown or definitively adult * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes * @param enc_attributes encrypted attribute data + * @param require_aml true to trigger AML * @return database transaction status */ enum GNUNET_DB_QueryStatus TEH_PG_insert_kyc_attributes ( void *cls, + uint64_t process_row, const struct TALER_PaytoHashP *h_payto, const struct GNUNET_ShortHashCode *kyc_prox, const char *provider_section, - const char *birthdate, + uint32_t birthday, struct GNUNET_TIME_Timestamp collection_time, - struct GNUNET_TIME_Timestamp expiration_time, + const char *provider_account_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration_time, size_t enc_attributes_size, - const void *enc_attributes); + const void *enc_attributes, + bool require_aml); + #endif diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c index e16a4b74f..9baaf3b1a 100644 --- a/src/exchangedb/pg_insert_records_by_table.c +++ b/src/exchangedb/pg_insert_records_by_table.c @@ -1995,10 +1995,6 @@ irbt_cb_table_kyc_attributes (struct PostgresClosure *pg, &td->details.kyc_attributes.kyc_prox), GNUNET_PQ_query_param_string ( td->details.kyc_attributes.provider), - (NULL == td->details.kyc_attributes.birthdate) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string ( - td->details.kyc_attributes.birthdate), GNUNET_PQ_query_param_timestamp ( &td->details.kyc_attributes.collection_time), GNUNET_PQ_query_param_timestamp ( @@ -2016,12 +2012,11 @@ irbt_cb_table_kyc_attributes (struct PostgresClosure *pg, ",h_payto" ",kyc_prox" ",provider" - ",birthdate" ",collection_time" ",expiration_time" ",encrypted_attributes" ") VALUES " - "($1, $2, $3, $4, $5, $6, $7, $8);"); + "($1, $2, $3, $4, $5, $6, $7);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_into_table_kyc_attributes", params); diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c index 534e9a1d2..3fcad58c0 100644 --- a/src/exchangedb/pg_lookup_records_by_table.c +++ b/src/exchangedb/pg_lookup_records_by_table.c @@ -2684,11 +2684,6 @@ lrbt_cb_table_kyc_attributes (void *cls, GNUNET_PQ_result_spec_string ( "provider", &td.details.kyc_attributes.provider), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ( - "birthdate", - &td.details.kyc_attributes.birthdate), - NULL), GNUNET_PQ_result_spec_timestamp ( "collection_time", &td.details.kyc_attributes.collection_time), @@ -3577,7 +3572,6 @@ TEH_PG_lookup_records_by_table (void *cls, ",h_payto" ",kyc_prox" ",provider" - ",birthdate" ",collection_time" ",expiration_time" ",encrypted_attributes" diff --git a/src/exchangedb/pg_reserves_in_insert.c b/src/exchangedb/pg_reserves_in_insert.c index 691c57d38..72fde7499 100644 --- a/src/exchangedb/pg_reserves_in_insert.c +++ b/src/exchangedb/pg_reserves_in_insert.c @@ -615,3 +615,284 @@ TEH_PG_reserves_in_insert ( GNUNET_free (rrs[i].notify_s); return qs; } + + +#if 0 + +/** + * Closure for our helper_cb() + */ +struct Context +{ + /** + * Array of reserve UUIDs to initialize. + */ + uint64_t *reserve_uuids; + + /** + * Array with entries set to 'true' for duplicate transactions. + */ + bool *transaction_duplicates; + + /** + * Array with entries set to 'true' for rows with conflicts. + */ + bool *conflicts; + + /** + * Set to #GNUNET_SYSERR on failures. + */ + struct GNUNET_GenericReturnValue status; + + /** + * Single value (no array) set to true if we need + * to follow-up with an update. + */ + bool *needs_update; +}; + + +/** + * Helper function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct Context *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +helper_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct Context *ctx = cls; + + for (unsigned int i = 0; itransaction_duplicates[i]), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint64 ("ruuid", + &ctx->reserve_uuids[i]), + &ctx->conflicts[i]), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + ctx->status = GNUNET_SYSERR; + return; + } + *ctx->need_update |= ctx->conflicts[i]; + } +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_reserves_in_insertN ( + void *cls, + const struct TALER_EXCHANGEDB_ReserveInInfo *reserves, + unsigned int reserves_length, + enum GNUNET_DB_QueryStatus *results) +{ + struct PostgresClosure *pg = cls; + struct TALER_PaytoHashP h_paytos[GNUNET_NZL (reserves_length)]; + char *notify_s[GNUNET_NZL (reserves_length)]; + struct TALER_ReservePublicKeyP *reserve_pubs[GNUNET_NZL (reserves_length)]; + struct TALER_Amount *balances[GNUNET_NZL (reserves_length)]; + struct GNUNET_TIME_Timestamp execution_times[GNUNET_NZL (reserves_length)]; + const char *sender_account_details[GNUNET_NZL (reserves_length)]; + const char *exchange_account_names[GNUNET_NZL (reserves_length)]; + uint64_t wire_references[GNUNET_NZL (reserves_length)]; + uint64_t reserve_uuids[GNUNET_NZL (reserves_length)]; + bool transaction_duplicates[GNUNET_NZL (reserves_length)]; + bool conflicts[GNUNET_NZL (reserves_length)]; + struct GNUNET_TIME_Timestamp reserve_expiration + = GNUNET_TIME_relative_to_timestamp (pg->idle_reserve_expiration_time); + struct GNUNET_TIME_Timestamp gc + = GNUNET_TIME_relative_to_timestamp (pg->legal_reserve_expiration_time); + bool needs_update = false; + enum GNUNET_DB_QueryStatus qs; + + for (unsigned int i = 0; isender_account_details, + &h_paytos[i]); + notify_s[i] = compute_notify_on_reserve (reserve->reserve_pub); + reserve_pubs[i] = &reserve->reserve_pub; + balances[i] = &reserve->balance; + execution_times[i] = reserve->execution_time; + sender_account_details[i] = reserve->sender_account_details; + exchange_account_names[i] = reserve->exchange_account_name; + wire_references[i] = reserve->wire_reference; + } + + /* NOTE: kind-of pointless to explicitly start a transaction here... */ + if (GNUNET_OK != + TEH_PG_preflight (pg)) + { + GNUNET_break (0); + qs = GNUNET_DB_STATUS_HARD_ERROR; + goto finished; + } + + if (GNUNET_OK != + TEH_PG_start_read_committed (pg, + "READ_COMMITED")) + { + GNUNET_break (0); + qs = GNUNET_DB_STATUS_HARD_ERROR; + goto finished; + } + + PREPARE (pg, + "reserves_insert_with_array", + "SELECT" + " transaction_duplicate" + ",ruuid" + "FROM exchange_do_array_reserves_insert" + " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);"); + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_timestamp (&gc), + GNUNET_PQ_query_param_timestamp (&reserve_expiration), + GNUNET_PQ_query_param_array_auto_from_type (reserves_length, + reserve_pubs, + pg->conn), + GNUNET_PQ_query_param_array_uint64 (reserves_length, + wire_references, + pg->conn), + TALER_PQ_query_param_array_amount (reserves_length, + balances, + pg->conn), + GNUNET_PQ_query_param_array_string (reserves_length, + exchange_account_names, + pg->conn), + GNUNET_PQ_query_param_array_timestamp (reserves_length, + execution_times, + pg->conn), + GNUNET_PQ_query_param_array_bytes_same_size_cont_auto ( + reserves_length, + h_paytos, + sizeof (struct GNUNET_PaytoHashP), + pg->conn), + GNUNET_PQ_query_param_array_string (reserves_length, + sender_account_details, + pg->conn), + GNUNET_PQ_query_param_array_string (reserves_length, + notify_s, + pg->conn), + GNUNET_PQ_query_param_end + }; + struct Context ctx = { + .reserve_uuids = reserve_uuids, + .transaction_duplicates = transaction_duplicates, + .conflicts = conflicts, + .needs_update = &needs_update, + .status = GNUNET_OK + }; + + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "reserves_insert_with_array", + params, + &multi_res, + &ctx); + if ( (qs < 0) || + (GNUNET_OK != ctx.status) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to insert into reserves (%d)\n", + qs); + goto finished; + } + } + + { + enum GNUNET_DB_QueryStatus cs; + + cs = TEH_PG_commit (pg); + if (cs < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to commit\n"); + qs = cs; + goto finished; + } + } + for (unsigned int i = 0; iconn, + "reserves_update", + params, + rs); + if (qs < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to update reserves (%d)\n", + qs); + results[i] = qs; + goto finished; + } + results[i] = duplicate + ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS + : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; + } + } +finished: + for (unsigned int i = 0; icb (ctx->cb_cls, ctx->h_payto, provider, - birthdate, collection_time, expiration_time, enc_attributes_size, @@ -145,7 +139,6 @@ TEH_PG_select_kyc_attributes ( "select_kyc_attributes", "SELECT " " provider" - ",birthdate" ",collection_time" ",expiration_time" ",encrypted_attributes" diff --git a/src/exchangedb/pg_select_similar_kyc_attributes.c b/src/exchangedb/pg_select_similar_kyc_attributes.c index a07f2a147..342f9ef33 100644 --- a/src/exchangedb/pg_select_similar_kyc_attributes.c +++ b/src/exchangedb/pg_select_similar_kyc_attributes.c @@ -76,16 +76,11 @@ get_attributes_cb (void *cls, size_t enc_attributes_size; void *enc_attributes; char *provider; - char *birthdate = NULL; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("h_payto", &h_payto), GNUNET_PQ_result_spec_string ("provider", &provider), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("birthdate", - &birthdate), - NULL), GNUNET_PQ_result_spec_timestamp ("collection_time", &collection_time), GNUNET_PQ_result_spec_timestamp ("expiration_time", @@ -108,7 +103,6 @@ get_attributes_cb (void *cls, ctx->cb (ctx->cb_cls, &h_payto, provider, - birthdate, collection_time, expiration_time, enc_attributes_size, @@ -143,7 +137,6 @@ TEH_PG_select_similar_kyc_attributes ( "SELECT " " h_payto" ",provider" - ",birthdate" ",collection_time" ",expiration_time" ",encrypted_attributes" diff --git a/src/exchangedb/pg_update_kyc_attributes.c b/src/exchangedb/pg_update_kyc_attributes.c deleted file mode 100644 index f77eb2bfc..000000000 --- a/src/exchangedb/pg_update_kyc_attributes.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see - */ -/** - * @file exchangedb/pg_update_kyc_attributes.c - * @brief Implementation of the update_kyc_attributes function for Postgres - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_error_codes.h" -#include "taler_dbevents.h" -#include "taler_pq_lib.h" -#include "pg_update_kyc_attributes.h" -#include "pg_helper.h" - - -enum GNUNET_DB_QueryStatus -TEH_PG_update_kyc_attributes ( - void *cls, - const struct TALER_PaytoHashP *h_payto, - const struct GNUNET_ShortHashCode *kyc_prox, - const char *provider_section, - const char *birthdate, - struct GNUNET_TIME_Timestamp collection_time, - struct GNUNET_TIME_Timestamp expiration_time, - size_t enc_attributes_size, - const void *enc_attributes) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (h_payto), - GNUNET_PQ_query_param_auto_from_type (kyc_prox), - GNUNET_PQ_query_param_string (provider_section), - (NULL == birthdate) - ? GNUNET_PQ_query_param_null () - : GNUNET_PQ_query_param_string (birthdate), - GNUNET_PQ_query_param_timestamp (&collection_time), - GNUNET_PQ_query_param_timestamp (&expiration_time), - GNUNET_PQ_query_param_fixed_size (enc_attributes, - enc_attributes_size), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "update_kyc_attributes", - "UPDATE kyc_attributes SET " - " kyc_prox=$2" - ",birthdate=$4" - ",collection_time=$5" - ",expiration_time=$6" - ",encrypted_attributes=$7" - " WHERE h_payto=$1 AND provider_section=$3;"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "update_kyc_attributes", - params); -} diff --git a/src/exchangedb/pg_update_kyc_attributes.h b/src/exchangedb/pg_update_kyc_attributes.h deleted file mode 100644 index 5d17eb7fa..000000000 --- a/src/exchangedb/pg_update_kyc_attributes.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see - */ -/** - * @file exchangedb/pg_update_kyc_attributes.h - * @brief implementation of the update_kyc_attributes function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_UPDATE_KYC_ATTRIBUTES_H -#define PG_UPDATE_KYC_ATTRIBUTES_H - -#include "taler_util.h" -#include "taler_json_lib.h" -#include "taler_exchangedb_plugin.h" - - -/** - * Update KYC attribute data. - * - * @param cls closure - * @param h_payto account for which the attribute data is stored - * @param kyc_prox key for similarity search - * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown - * @param collection_time when was the data collected - * @param expiration_time when does the data expire - * @param enc_attributes_size number of bytes in @a enc_attributes - * @param enc_attributes encrypted attribute data - * @return database transaction status - */ -enum GNUNET_DB_QueryStatus -TEH_PG_update_kyc_attributes ( - void *cls, - const struct TALER_PaytoHashP *h_payto, - const struct GNUNET_ShortHashCode *kyc_prox, - const char *provider_section, - const char *birthdate, - struct GNUNET_TIME_Timestamp collection_time, - struct GNUNET_TIME_Timestamp expiration_time, - size_t enc_attributes_size, - const void *enc_attributes); - - -#endif diff --git a/src/exchangedb/pg_update_kyc_process_by_row.c b/src/exchangedb/pg_update_kyc_process_by_row.c index 4ae44ce53..711f47802 100644 --- a/src/exchangedb/pg_update_kyc_process_by_row.c +++ b/src/exchangedb/pg_update_kyc_process_by_row.c @@ -68,7 +68,8 @@ TEH_PG_update_kyc_process_by_row ( if (qs <= 0) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to update legitimization process: %d\n", + "Failed to update legitimization process %llu: %d\n", + (unsigned long long) process_row, qs); return qs; } diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index b3ebc7547..006484198 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -207,7 +207,6 @@ #include "pg_setup_wire_target.h" #include "pg_compute_shard.h" #include "pg_insert_kyc_attributes.h" -#include "pg_update_kyc_attributes.h" #include "pg_select_similar_kyc_attributes.h" #include "pg_select_kyc_attributes.h" #include "pg_insert_aml_officer.h" @@ -754,8 +753,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_set_purse_balance; plugin->insert_kyc_attributes = &TEH_PG_insert_kyc_attributes; - plugin->update_kyc_attributes - = &TEH_PG_update_kyc_attributes; plugin->select_similar_kyc_attributes = &TEH_PG_select_similar_kyc_attributes; plugin->select_kyc_attributes diff --git a/src/exchangedb/procedures.sql.in b/src/exchangedb/procedures.sql.in index 12ec3656f..c9277ea60 100644 --- a/src/exchangedb/procedures.sql.in +++ b/src/exchangedb/procedures.sql.in @@ -39,6 +39,7 @@ SET search_path TO exchange; #include "exchange_do_insert_or_update_policy_details.sql" #include "exchange_do_insert_aml_decision.sql" #include "exchange_do_insert_aml_officer.sql" +#include "exchange_do_insert_kyc_attributes.sql" #include "exchange_do_reserves_in_insert.sql" #include "exchange_do_batch_reserves_update.sql" #include "exchange_do_refund_by_coin.sql" diff --git a/src/include/taler_amount_lib.h b/src/include/taler_amount_lib.h index 04aa00045..c9a35a2aa 100644 --- a/src/include/taler_amount_lib.h +++ b/src/include/taler_amount_lib.h @@ -402,7 +402,7 @@ TALER_amount_multiply (struct TALER_Amount *result, * #GNUNET_NO if value was already normalized * #GNUNET_SYSERR if value was invalid or could not be normalized */ -int +enum GNUNET_GenericReturnValue TALER_amount_normalize (struct TALER_Amount *amount); diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 3769315e8..6d4ca1091 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -366,6 +366,10 @@ struct TALER_EXCHANGE_Keys */ char *asset_type; + /** + * Set to true if tipping is allowed at this exchange. + */ + bool tipping_allowed; }; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 3a6ba6514..92fb36fb2 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -756,7 +756,6 @@ struct TALER_EXCHANGEDB_TableData struct TALER_PaytoHashP h_payto; struct GNUNET_ShortHashCode kyc_prox; char *provider; - char *birthdate; /* NULL allowed! */ struct GNUNET_TIME_Timestamp collection_time; struct GNUNET_TIME_Timestamp expiration_time; void *encrypted_attributes; @@ -2429,8 +2428,6 @@ typedef void * @param cls closure * @param h_payto account for which the attribute data is stored * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes @@ -2441,7 +2438,6 @@ typedef void void *cls, const struct TALER_PaytoHashP *h_payto, const char *provider_section, - const char *birthdate, struct GNUNET_TIME_Timestamp collection_time, struct GNUNET_TIME_Timestamp expiration_time, size_t enc_attributes_size, @@ -6765,59 +6761,39 @@ struct TALER_EXCHANGEDB_Plugin /** - * Store KYC attribute data. + * Store KYC attribute data, update KYC process status and + * AML status for the given account. * * @param cls closure + * @param process_row KYC process row to update * @param h_payto account for which the attribute data is stored * @param kyc_prox key for similarity search * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown + * @param provider_account_id provider account ID + * @param provider_legitimization_id provider legitimization ID + * @param birthday birthdate of user, in days after 1990, or 0 if unknown or definitively adult * @param collection_time when was the data collected * @param expiration_time when does the data expire * @param enc_attributes_size number of bytes in @a enc_attributes * @param enc_attributes encrypted attribute data + * @param require_aml true to trigger AML * @return database transaction status */ enum GNUNET_DB_QueryStatus (*insert_kyc_attributes)( void *cls, + uint64_t process_row, const struct TALER_PaytoHashP *h_payto, const struct GNUNET_ShortHashCode *kyc_prox, const char *provider_section, - const char *birthdate, + uint32_t birthday, struct GNUNET_TIME_Timestamp collection_time, - struct GNUNET_TIME_Timestamp expiration_time, + const char *provider_account_id, + const char *provider_legitimization_id, + struct GNUNET_TIME_Absolute expiration_time, size_t enc_attributes_size, - const void *enc_attributes); - - - /** - * Update KYC attribute data. - * - * @param cls closure - * @param h_payto account for which the attribute data is stored - * @param kyc_prox key for similarity search - * @param provider_section provider that must be checked - * @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL; - * digits can be 0 if exact day, month or year are unknown - * @param collection_time when was the data collected - * @param expiration_time when does the data expire - * @param enc_attributes_size number of bytes in @a enc_attributes - * @param enc_attributes encrypted attribute data - * @return database transaction status - */ - enum GNUNET_DB_QueryStatus - (*update_kyc_attributes)( - void *cls, - const struct TALER_PaytoHashP *h_payto, - const struct GNUNET_ShortHashCode *kyc_prox, - const char *provider_section, - const char *birthdate, - struct GNUNET_TIME_Timestamp collection_time, - struct GNUNET_TIME_Timestamp expiration_time, - size_t enc_attributes_size, - const void *enc_attributes); + const void *enc_attributes, + bool require_aml); /** diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am index 20430a4ea..75ea13b6f 100644 --- a/src/kyclogic/Makefile.am +++ b/src/kyclogic/Makefile.am @@ -20,6 +20,7 @@ EXTRA_DIST = \ persona-sample-reply.json bin_SCRIPTS = \ + taler-exchange-kyc-kycaid-converter.sh \ taler-exchange-kyc-persona-converter.sh lib_LTLIBRARIES = \ diff --git a/src/kyclogic/kyclogic-kycaid.conf b/src/kyclogic/kyclogic-kycaid.conf index 0e1fe96ef..753fb689d 100644 --- a/src/kyclogic/kyclogic-kycaid.conf +++ b/src/kyclogic/kyclogic-kycaid.conf @@ -12,6 +12,10 @@ PROVIDED_CHECKS = EXAMPLE_DO_NOT_USE # How long is the KYC check valid? KYC_KYCAID_VALIDITY = forever +# Program that converts Persona KYC data into the +# GNU Taler format. +KYC_KYCAID_CONVERTER_HELPER = taler-exchange-kyc-kycaid-converter.sh + # Authentication token to use. KYC_KYCAID_AUTH_TOKEN = XXX diff --git a/src/kyclogic/kyclogic-oauth2.conf b/src/kyclogic/kyclogic-oauth2.conf index 6f83c0e44..61b38367f 100644 --- a/src/kyclogic/kyclogic-oauth2.conf +++ b/src/kyclogic/kyclogic-oauth2.conf @@ -13,11 +13,11 @@ PROVIDED_CHECKS = EXAMPLE_DO_NOT_USE KYC_OAUTH2_VALIDITY = forever # URL where we initiate the user's login process -KYC_OAUTH2_LOGIN_URL = http://kyc.example.com/login +KYC_OAUTH2_AUTHORIZE_URL = https://kyc.example.com/authorize # URL where we send the user's authentication information -KYC_OAUTH2_AUTH_URL = http://kyc.example.com/auth +KYC_OAUTH2_TOKEN_URL = https://kyc.example.com/token # URL of the user info access point. -KYC_OAUTH2_INFO_URL = http://kyc.example.com/info +KYC_OAUTH2_INFO_URL = https://kyc.example.com/info # Where does the client get redirected upon completion? KYC_OAUTH2_POST_URL = http://example.com/thank-you diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c b/src/kyclogic/plugin_kyclogic_kycaid.c index 3273a51f1..95dc4bb78 100644 --- a/src/kyclogic/plugin_kyclogic_kycaid.c +++ b/src/kyclogic/plugin_kyclogic_kycaid.c @@ -87,6 +87,12 @@ struct TALER_KYCLOGIC_ProviderDetails */ char *form_id; + /** + * Helper binary to convert attributes returned by + * KYCAID into our internal format. + */ + char *conversion_helper; + /** * Validity time for a successful KYC process. */ @@ -215,6 +221,12 @@ struct TALER_KYCLOGIC_WebhookHandle */ struct PluginState *ps; + /** + * Handle to helper process to extract attributes + * we care about. + */ + struct TALER_JSON_ExternalConversion *econ; + /** * Our configuration details. */ @@ -225,6 +237,11 @@ struct TALER_KYCLOGIC_WebhookHandle */ struct MHD_Connection *connection; + /** + * JSON response we got back, or NULL for none. + */ + json_t *json_response; + /** * Verification ID from the service. */ @@ -261,6 +278,11 @@ struct TALER_KYCLOGIC_WebhookHandle */ uint64_t process_row; + /** + * HTTP response code we got from KYCAID. + */ + unsigned int kycaid_response_code; + /** * HTTP response code to return asynchronously. */ @@ -277,6 +299,7 @@ static void kycaid_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd) { curl_slist_free_all (pd->slist); + GNUNET_free (pd->conversion_helper); GNUNET_free (pd->auth_token); GNUNET_free (pd->form_id); GNUNET_free (pd->section); @@ -337,6 +360,18 @@ kycaid_load_configuration (void *cls, kycaid_unload_configuration (pd); return NULL; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (ps->cfg, + provider_section_name, + "KYC_KYCAID_CONVERTER_HELPER", + &pd->conversion_helper)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + provider_section_name, + "KYC_KYCAID_CONVERTER_HELPER"); + kycaid_unload_configuration (pd); + return NULL; + } { char *auth; @@ -695,11 +730,21 @@ kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh) GNUNET_SCHEDULER_cancel (wh->task); wh->task = NULL; } + if (NULL != wh->econ) + { + TALER_JSON_external_conversion_stop (wh->econ); + wh->econ = NULL; + } if (NULL != wh->job) { GNUNET_CURL_job_cancel (wh->job); wh->job = NULL; } + if (NULL != wh->json_response) + { + json_decref (wh->json_response); + wh->json_response = NULL; + } GNUNET_free (wh->verification_id); GNUNET_free (wh->applicant_id); GNUNET_free (wh->url); @@ -750,6 +795,97 @@ log_failure (json_t *verifications) } +/** + * Type of a callback that receives a JSON @a result. + * + * @param cls closure our `struct TALER_KYCLOGIC_WebhookHandle *` + * @param status_type how did the process die + * @param code termination status code from the process + * @param result converted attribute data, NULL on failure + */ +static void +webhook_conversion_cb (void *cls, + enum GNUNET_OS_ProcessStatusType status_type, + unsigned long code, + const json_t *result) +{ + struct TALER_KYCLOGIC_WebhookHandle *wh = cls; + struct GNUNET_TIME_Absolute expiration; + struct MHD_Response *resp; + + wh->econ = NULL; + if ( (0 == code) || + (NULL == result) ) + { + /* No result, but *our helper* was OK => bad input */ + GNUNET_break_op (0); + json_dumpf (wh->json_response, + stderr, + JSON_INDENT (2)); + resp = TALER_MHD_MAKE_JSON_PACK ( + GNUNET_JSON_pack_uint64 ("kycaid_http_status", + wh->kycaid_response_code), + GNUNET_JSON_pack_object_incref ("kycaid_body", + (json_t *) wh->json_response)); + wh->cb (wh->cb_cls, + wh->process_row, + &wh->h_payto, + wh->pd->section, + wh->applicant_id, + wh->verification_id, + TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, + GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ + NULL, + MHD_HTTP_BAD_GATEWAY, + resp); + kycaid_webhook_cancel (wh); + return; + } + if (NULL == result) + { + /* Failure in our helper */ + GNUNET_break (0); + json_dumpf (wh->json_response, + stderr, + JSON_INDENT (2)); + resp = TALER_MHD_MAKE_JSON_PACK ( + GNUNET_JSON_pack_uint64 ("kycaid_http_status", + wh->kycaid_response_code), + GNUNET_JSON_pack_object_incref ("kycaid_body", + (json_t *) wh->json_response)); + wh->cb (wh->cb_cls, + wh->process_row, + &wh->h_payto, + wh->pd->section, + wh->applicant_id, + wh->verification_id, + TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, + GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ + NULL, + MHD_HTTP_BAD_GATEWAY, + resp); + kycaid_webhook_cancel (wh); + return; + } + expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity); + resp = MHD_create_response_from_buffer (0, + "", + MHD_RESPMEM_PERSISTENT); + wh->cb (wh->cb_cls, + wh->process_row, + &wh->h_payto, + wh->pd->section, + wh->applicant_id, + wh->verification_id, + TALER_KYCLOGIC_STATUS_SUCCESS, + expiration, + result, + MHD_HTTP_NO_CONTENT, + resp); + kycaid_webhook_cancel (wh); +} + + /** * Function called when we're done processing the * HTTP "/applicants/{verification_id}" request. @@ -768,246 +904,20 @@ handle_webhook_finished (void *cls, struct MHD_Response *resp; wh->job = NULL; + wh->kycaid_response_code = response_code; + wh->json_response = json_incref ((json_t *) j); switch (response_code) { case MHD_HTTP_OK: { - const char *type; const char *profile_status; - const char *first_name = NULL; - const char *last_name = NULL; - const char *middle_name = NULL; - const char *dob = NULL; - const char *residence_country = NULL; - const char *gender = NULL; - bool pep = false; - bool no_pep = false; - const char *company_name = NULL; - const char *business_activity_id = NULL; - const char *registration_country = NULL; - const char *email = NULL; - const char *phone = NULL; - json_t *addresses = NULL; - json_t *documents = NULL; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("type", - &type), - GNUNET_JSON_spec_string ("profile_status", - &profile_status), /* valid, invalid, pending */ - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("email", - &email), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("phone", - &phone), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("addresses", - &addresses), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("documents", - &documents), - NULL), - GNUNET_JSON_spec_end () - }; - struct GNUNET_JSON_Specification bspec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("company_name", - &company_name), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("business_activity_id", - &business_activity_id), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("registration_country", - ®istration_country), - NULL), - GNUNET_JSON_spec_end () - }; - struct GNUNET_JSON_Specification pspec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("first_name", - &first_name), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("middle_name", - &middle_name), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("last_name", - &last_name), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("dob", - &dob), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("residence_country", - &residence_country), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("gender", - &gender), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_bool ("pep", - &pep), - &no_pep), - GNUNET_JSON_spec_end () - }; - struct GNUNET_JSON_Specification *ispec = NULL; - struct GNUNET_TIME_Absolute expiration; - bool no_parse; - enum TALER_KYCLOGIC_KycUserType ut; - no_parse = (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)); - if (! no_parse) - { - ut = (0 == strcasecmp ("person", - type)) - ? TALER_KYCLOGIC_KYC_UT_INDIVIDUAL - : TALER_KYCLOGIC_KYC_UT_BUSINESS; - ispec = (ut == TALER_KYCLOGIC_KYC_UT_INDIVIDUAL) - ? pspec - : bspec; - no_parse = (GNUNET_OK != - GNUNET_JSON_parse (j, - ispec, - NULL, NULL)); - } - if (no_parse) - { - GNUNET_break_op (0); - json_dumpf (j, - stderr, - JSON_INDENT (2)); - resp = TALER_MHD_MAKE_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("kycaid_http_status", - response_code), - GNUNET_JSON_pack_object_incref ("kycaid_body", - (json_t *) j)); - wh->cb (wh->cb_cls, - wh->process_row, - &wh->h_payto, - wh->pd->section, - wh->applicant_id, - wh->verification_id, - TALER_KYCLOGIC_STATUS_PROVIDER_FAILED, - GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */ - NULL, - MHD_HTTP_BAD_GATEWAY, - resp); - break; - } - if (0 == strcasecmp ("valid", + profile_status = json_string_value ( + json_object_get ( + j, + "profile_status")); + if (0 != strcasecmp ("valid", profile_status)) - { - log_failure (json_object_get (j, - "decline_reasons")); - } - resp = MHD_create_response_from_buffer (0, - "", - MHD_RESPMEM_PERSISTENT); - if (0 == strcasecmp ("valid", - profile_status)) - { - json_t *attr; - - if (ut == TALER_KYCLOGIC_KYC_UT_INDIVIDUAL) - { - char *name = NULL; - - if ( (NULL != last_name) || - (NULL != first_name) || - (NULL != middle_name) ) - { - GNUNET_asprintf (&name, - "%s, %s %s", - (NULL != last_name) - ? last_name - : "", - (NULL != first_name) - ? first_name - : "", - (NULL != middle_name) - ? middle_name - : ""); - } - attr = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_BIRTHDATE, - dob)), - GNUNET_JSON_pack_allow_null ( - no_pep - ? GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_PEP, - NULL) - : GNUNET_JSON_pack_bool ( - TALER_ATTRIBUTE_PEP, - pep)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_FULL_NAME, - name)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_PHONE, - phone)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_EMAIL, - email)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_RESIDENCES, - residence_country)) - ); - GNUNET_free (name); - } - else - { - attr = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_COMPANY_NAME, - company_name)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_PHONE, - phone)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_EMAIL, - email)), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ( - TALER_ATTRIBUTE_REGISTRATION_COUNTRY, - residence_country)) - ); - } - // FIXME: do something about addresses & documents! - expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity); - wh->cb (wh->cb_cls, - wh->process_row, - &wh->h_payto, - wh->pd->section, - wh->applicant_id, - wh->verification_id, - TALER_KYCLOGIC_STATUS_SUCCESS, - expiration, - attr, - MHD_HTTP_NO_CONTENT, - resp); - json_decref (attr); - } - else { enum TALER_KYCLOGIC_KycStatus ks; @@ -1015,6 +925,9 @@ handle_webhook_finished (void *cls, profile_status)) ? TALER_KYCLOGIC_STATUS_PENDING : TALER_KYCLOGIC_STATUS_USER_ABORTED; + resp = MHD_create_response_from_buffer (0, + "", + MHD_RESPMEM_PERSISTENT); wh->cb (wh->cb_cls, wh->process_row, &wh->h_payto, @@ -1026,9 +939,19 @@ handle_webhook_finished (void *cls, NULL, MHD_HTTP_NO_CONTENT, resp); + break; } - GNUNET_JSON_parse_free (ispec); - GNUNET_JSON_parse_free (spec); + wh->econ + = TALER_JSON_external_conversion_start ( + j, + &webhook_conversion_cb, + wh, + wh->pd->conversion_helper, + wh->pd->conversion_helper, + "-a", + wh->pd->auth_token, + NULL); + return; } break; case MHD_HTTP_BAD_REQUEST: diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c index c72b04b7c..e7350f01c 100644 --- a/src/kyclogic/plugin_kyclogic_oauth2.c +++ b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-2023 Taler Systems SA Taler is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -75,15 +75,21 @@ struct TALER_KYCLOGIC_ProviderDetails char *section; /** - * URL of the OAuth2.0 endpoint for KYC checks. - * (token/auth) + * URL of the Challenger ``/setup`` endpoint for + * approving address validations. NULL if not used. */ - char *auth_url; + char *setup_url; /** * URL of the OAuth2.0 endpoint for KYC checks. */ - char *login_url; + char *authorize_url; + + /** + * URL of the OAuth2.0 endpoint for KYC checks. + * (token/auth) + */ + char *token_url; /** * URL of the user info access endpoint. @@ -147,6 +153,11 @@ struct TALER_KYCLOGIC_InitiateHandle */ struct GNUNET_SCHEDULER_Task *task; + /** + * Handle for the OAuth 2.0 setup request. + */ + struct GNUNET_CURL_Job *job; + /** * Continuation to call. */ @@ -283,8 +294,9 @@ static void oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd) { GNUNET_free (pd->section); - GNUNET_free (pd->auth_url); - GNUNET_free (pd->login_url); + GNUNET_free (pd->token_url); + GNUNET_free (pd->setup_url); + GNUNET_free (pd->authorize_url); GNUNET_free (pd->info_url); GNUNET_free (pd->client_id); GNUNET_free (pd->client_secret); @@ -327,12 +339,12 @@ oauth2_load_configuration (void *cls, if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (ps->cfg, provider_section_name, - "KYC_OAUTH2_AUTH_URL", + "KYC_OAUTH2_TOKEN_URL", &s)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, provider_section_name, - "KYC_OAUTH2_AUTH_URL"); + "KYC_OAUTH2_TOKEN_URL"); oauth2_unload_configuration (pd); return NULL; } @@ -346,23 +358,23 @@ oauth2_load_configuration (void *cls, { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, provider_section_name, - "KYC_OAUTH2_AUTH_URL", + "KYC_OAUTH2_TOKEN_URL", "not a valid URL"); GNUNET_free (s); oauth2_unload_configuration (pd); return NULL; } - pd->auth_url = s; + pd->token_url = s; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (ps->cfg, provider_section_name, - "KYC_OAUTH2_LOGIN_URL", + "KYC_OAUTH2_AUTHORIZE_URL", &s)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, provider_section_name, - "KYC_OAUTH2_LOGIN_URL"); + "KYC_OAUTH2_AUTHORIZE_URL"); oauth2_unload_configuration (pd); return NULL; } @@ -376,13 +388,41 @@ oauth2_load_configuration (void *cls, { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, provider_section_name, - "KYC_OAUTH2_LOGIN_URL", + "KYC_OAUTH2_AUTHORIZE_URL", "not a valid URL"); oauth2_unload_configuration (pd); GNUNET_free (s); return NULL; } - pd->login_url = s; + if (NULL != strchr (s, '#')) + { + const char *extra = strchr (s, '#'); + const char *slash = strrchr (s, '/'); + + if ( (0 != strcmp (extra, + "#setup")) || + (NULL == slash) ) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + provider_section_name, + "KYC_OAUTH2_AUTHORIZE_URL", + "not a valid authorze URL (bad fragment)"); + oauth2_unload_configuration (pd); + GNUNET_free (s); + return NULL; + } + pd->authorize_url = GNUNET_strndup (s, + extra - s); + GNUNET_asprintf (&pd->setup_url, + "%.*s/setup", + (int) (slash - s), + s); + GNUNET_free (s); + } + else + { + pd->authorize_url = s; + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (ps->cfg, @@ -480,19 +520,20 @@ oauth2_load_configuration (void *cls, * how to begin the OAuth2.0 checking process to * the client. * - * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *` + * @param ih process to redirect for + * @param authorize_url authorization URL to use */ static void -initiate_task (void *cls) +initiate_with_url (struct TALER_KYCLOGIC_InitiateHandle *ih, + const char *authorize_url) { - struct TALER_KYCLOGIC_InitiateHandle *ih = cls; + const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd; struct PluginState *ps = pd->ps; char *hps; char *url; char legi_s[42]; - ih->task = NULL; GNUNET_snprintf (legi_s, sizeof (legi_s), "%llu", @@ -515,16 +556,11 @@ initiate_task (void *cls) } GNUNET_asprintf (&url, "%s?response_type=code&client_id=%s&redirect_uri=%s", - pd->login_url, + authorize_url, pd->client_id, redirect_uri_encoded); GNUNET_free (redirect_uri_encoded); } - /* FIXME-API: why do we *redirect* the client here, - instead of making the HTTP request *ourselves* - and forwarding the response? This prevents us - from using authentication on initiation, - (which is desirable for challenger!) */ ih->cb (ih->cb_cls, TALER_EC_NONE, url, @@ -537,6 +573,142 @@ initiate_task (void *cls) } +/** + * After we are done with the CURL interaction we + * need to update our database state with the information + * retrieved. + * + * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *` + * @param response_code HTTP response code from server, 0 on hard error + * @param response in JSON, NULL if response was not in JSON format + */ +static void +handle_curl_setup_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_KYCLOGIC_InitiateHandle *ih = cls; + const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd; + const json_t *j = response; + + ih->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + { + const char *nonce; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("nonce", + &nonce), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + const char *emsg; + unsigned int line; + char *url; + + res = GNUNET_JSON_parse (j, + spec, + &emsg, + &line); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + json_dumpf (j, + stderr, + JSON_INDENT (2)); + ih->cb (ih->cb_cls, + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, + NULL, + NULL, + NULL, + "Unexpected response from KYC gateway: setup must return a nonce"); + GNUNET_free (ih); + return; + } + GNUNET_asprintf (&url, + "%s/%s", + pd->setup_url, + nonce); + initiate_with_url (ih, + url); + GNUNET_free (url); + return; + } + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "/setup URL returned HTTP status %u\n", + (unsigned int) response_code); + ih->cb (ih->cb_cls, + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, + NULL, + NULL, + NULL, + "/setup request to OAuth 2.0 backend returned unexpected HTTP status code"); + GNUNET_free (ih); + return; + } +} + + +/** + * Logic to asynchronously return the response for how to begin the OAuth2.0 + * checking process to the client. May first request a dynamic URL via + * ``/setup`` if configured to use a client-authenticated setup process. + * + * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *` + */ +static void +initiate_task (void *cls) +{ + struct TALER_KYCLOGIC_InitiateHandle *ih = cls; + const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd; + struct PluginState *ps = pd->ps; + char *hdr; + struct curl_slist *slist; + CURL *eh; + + ih->task = NULL; + if (NULL == pd->setup_url) + { + initiate_with_url (ih, + pd->authorize_url); + return; + } + eh = curl_easy_init (); + if (NULL == eh) + { + GNUNET_break (0); + ih->cb (ih->cb_cls, + TALER_EC_GENERIC_ALLOCATION_FAILURE, + NULL, + NULL, + NULL, + "curl_easy_init() failed"); + GNUNET_free (ih); + return; + } + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + pd->setup_url)); + GNUNET_asprintf (&hdr, + "%s: Bearer %s", + MHD_HTTP_HEADER_AUTHORIZATION, + pd->client_secret); + slist = curl_slist_append (NULL, + hdr); + ih->job = GNUNET_CURL_job_add2 (ps->curl_ctx, + eh, + slist, + &handle_curl_setup_finished, + ih); + curl_slist_free_all (slist); + GNUNET_free (hdr); +} + + /** * Initiate KYC check. * @@ -584,6 +756,11 @@ oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih) GNUNET_SCHEDULER_cancel (ih->task); ih->task = NULL; } + if (NULL != ih->job) + { + GNUNET_CURL_job_cancel (ih->job); + ih->job = NULL; + } GNUNET_free (ih); } @@ -1002,7 +1179,7 @@ handle_curl_login_finished (void *cls, eh = curl_easy_init (); if (NULL == eh) { - GNUNET_break_op (0); + GNUNET_break (0); ph->response = TALER_MHD_make_error ( TALER_EC_GENERIC_ALLOCATION_FAILURE, @@ -1129,7 +1306,7 @@ oauth2_proof (void *cls, GNUNET_assert (CURLE_OK == curl_easy_setopt (ph->eh, CURLOPT_URL, - pd->auth_url)); + pd->token_url)); GNUNET_assert (CURLE_OK == curl_easy_setopt (ph->eh, CURLOPT_POST, diff --git a/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh b/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh new file mode 100644 index 000000000..96aca2b80 --- /dev/null +++ b/src/kyclogic/taler-exchange-kyc-kycaid-converter.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# This file is in the public domain. +# +# This code converts (some of) the JSON output from KYCAID into the GNU Taler +# specific KYC attribute data (again in JSON format). We may need to download +# and inline file data in the process, for authorization pass "-a" with the +# respective bearer token. +# + +# Die if anything goes wrong. +set -eu + +# Parse command-line options +while getopts ':a:' OPTION; do + case "$OPTION" in + a) + TOKEN="$OPTARG" + ;; + ?) + echo "Unrecognized command line option" + exit 1 + ;; + esac +done + +# First, extract everything from stdin. +J=$(jq '{"type":.type,"email":.email,"phone":.phone,"first_name":.first_name,"name-middle":.middle_name,"last_name":.last_name,"dob":.dob,"residence_country":.residence_country,"gender":.gender,"pep":.pep,"addresses":.addresses,"documents":.documents,"company_name":.company_name,"business_activity_id":.business_activity_id,"registration_country":.registration_country,"documents":.documents,"decline_reasons":.decline_reasons}') + +# TODO: +# log_failure (json_object_get (j, "decline_reasons")); + +TYPE=$(echo "$J" | jq -r '.person') + +N=0 +DOCS_RAW="" +DOCS_JSON="" +for ID in $(jq -r '.documents[]|select(.status=="valid")|.id') +do + TYPE=$(jq -r ".documents[]|select(.id==\"$ID\")|.type") + EXPIRY=$(jq -r ".documents[]|select(.id==\"$ID\")|.expiry_date") + DOCUMENT_FILE=$(mktemp -t tmp.XXXXXXXXXX) + # Authoriazation: Token $TOKEN + DOCUMENT_URL="https://api.kycaid.com/documents/$ID" + if [ -z "${TOKEN:-}" ] + then + wget -q --output-document=- "$DOCUMENT_URL" \ + | gnunet-base32 > ${DOCUMENT_FILE} + else + wget -q --output-document=- "$DOCUMENT_URL" \ + --header "Authorization: Token $TOKEN" \ + | gnunet-base32 > ${DOCUMENT_FILE} + fi + DOCS_RAW="$DOCS_RAW --rawfile photo$N \"${DOCUMENT_FILE}\"" + if [ "$N" = 0 ] + then + DOCS_JSON="{\"type\":\"$TYPE\",\"image\":\$photo$N}" + else + DOCS_JSON="{\"type\":\"$TYPE\",\"image\":\$photo$N},$DOCS_JSON" + fi + N=$(expr $N + 1) +done + + +if [ "person" = "${TYPE}" ] +then + + # Next, combine some fields into larger values. + FULLNAME=$(echo "$J" | jq -r '[.first_name,.middle_name,.last_name]|join(" ")') +# STREET=$(echo $J | jq -r '[."street-1",."street-2"]|join(" ")') +# CITY=$(echo $J | jq -r '[.postcode,.city,."address-subdivision,.cc"]|join(" ")') + + # Combine into final result for individual. + # FIXME: does jq tolerate 'pep = NULL' here? + echo "$J" | jq \ + --arg full_name "${FULLNAME}" \ + '{$full_name,"birthdate":.dob,"pep":.pep,"phone":."phone","email",.email,"residences":.residence_country}' + +else + # Combine into final result for business. + echo "$J" | jq \ + --arg full_name "${FULLNAME}" \ + $DOCS_RAW \ + "{\"company_name\":.company_name,\"phone\":.phone,\"email\":.email,\"registration_country\":.registration_country,\"documents\":[${DOCS_JSON}]}" +fi + +exit 0 diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index 0e8046305..601b163db 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -733,6 +733,7 @@ decode_keys_json (const json_t *resp_obj, struct TALER_ExchangePublicKeyP pub; const char *currency; const char *asset_type; + bool tipping_allowed = true; json_t *wblwk = NULL; struct GNUNET_JSON_Specification mspec[] = { GNUNET_JSON_spec_fixed_auto ("denominations_sig", @@ -749,6 +750,10 @@ decode_keys_json (const json_t *resp_obj, ¤cy), GNUNET_JSON_spec_string ("asset_type", &asset_type), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_bool ("tipping_allowed", + &tipping_allowed), + NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("wallet_balance_limit_without_kyc", &wblwk), @@ -819,6 +824,7 @@ decode_keys_json (const json_t *resp_obj, NULL, NULL)); key_data->currency = GNUNET_strdup (currency); key_data->asset_type = GNUNET_strdup (asset_type); + key_data->tipping_allowed = tipping_allowed; /* parse the global fees */ { diff --git a/src/mhd/mhd_config.c b/src/mhd/mhd_config.c index 0e9f2e088..31ec3e476 100644 --- a/src/mhd/mhd_config.c +++ b/src/mhd/mhd_config.c @@ -78,7 +78,7 @@ TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg, if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, section, - "port", + "PORT", &port)) { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, diff --git a/src/sq/sq_query_helper.c b/src/sq/sq_query_helper.c index d4b2d060d..711e63816 100644 --- a/src/sq/sq_query_helper.c +++ b/src/sq/sq_query_helper.c @@ -150,7 +150,7 @@ qconv_json (void *cls, if (SQLITE_OK != sqlite3_bind_text (stmt, (int) off, str, - strlen (str) + 1, + strlen (str), SQLITE_TRANSIENT)) return GNUNET_SYSERR; GNUNET_free (str); diff --git a/src/templating/Makefile.am b/src/templating/Makefile.am index 6b5234dba..f960bdcca 100644 --- a/src/templating/Makefile.am +++ b/src/templating/Makefile.am @@ -15,6 +15,8 @@ taler_mustach_tool_SOURCES = \ taler_mustach_tool_LDADD = \ libmustach.la \ -ljansson +taler_mustach_tool_CFLAGS = \ + -DTOOL=MUSTACH_TOOL_JANSSON lib_LTLIBRARIES = \ libtalertemplating.la diff --git a/src/templating/Makefile.orig b/src/templating/Makefile.orig index 3a1d20736..e902b2c71 100644 --- a/src/templating/Makefile.orig +++ b/src/templating/Makefile.orig @@ -245,7 +245,7 @@ basic-tests: mustach @$(MAKE) -C test3 test @$(MAKE) -C test4 test @$(MAKE) -C test5 test - @$(MAKE) -C test6 test +# @$(MAKE) -C test6 test spec-tests: $(TESTSPECS) @@ -298,4 +298,3 @@ manuals: mustach.1.gz mustach.1.gz: mustach.1.scd if which scdoc >/dev/null 2>&1; then scdoc < mustach.1.scd | gzip > mustach.1.gz; fi - diff --git a/src/templating/README.md b/src/templating/README.md index 6699b0e89..7034ef99f 100644 --- a/src/templating/README.md +++ b/src/templating/README.md @@ -5,7 +5,7 @@ template specification. The main site for `mustach` is on [gitlab](https://gitlab.com/jobol/mustach). -The simpliest way to use mustach is to copy the files **mustach.h** and **mustach.c** +The simplest way to use mustach is to copy the files **mustach.h** and **mustach.c** directly into your project and use it. If you are using one of the JSON libraries listed below, you can get extended feature @@ -85,7 +85,7 @@ It then outputs the result of applying the templates files to the JSON file. ### Portability Some system does not provide *open_memstream*. In that case, tell your -prefered compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**. +preferred compiler to declare the preprocessor symbol **NO_OPEN_MEMSTREAM**. Example: CFLAGS=-DNO_OPEN_MEMSTREAM make @@ -163,7 +163,7 @@ Here is the summary. Flag name | Description -------------------------------+------------------------------------------------ - Mustach_With_Colon | Explicit tag substition with colon + Mustach_With_Colon | Explicit tag substitution with colon Mustach_With_EmptyTag | Empty Tag Allowed -------------------------------+------------------------------------------------ Mustach_With_Equal | Value Testing Equality @@ -180,7 +180,7 @@ For the details, see below. ### Explicit Tag Substitution With Colon (Mustach_With_Colon) -In somecases the name of the key used for substition begins with a +In somecases the name of the key used for substitution begins with a character reserved for mustach: one of `#`, `^`, `/`, `&`, `{`, `>` and `=`. This extension introduces the special character `:` to explicitly @@ -311,4 +311,3 @@ The table below summarize the changes. fdmustach_json_c | mustach_json_c_fd mustach_json_c | mustach_json_c_mem mustach_json_c | mustach_json_c_write - diff --git a/src/templating/mustach-tool.c b/src/templating/mustach-tool.c index 0c8f44070..83a0813e5 100644 --- a/src/templating/mustach-tool.c +++ b/src/templating/mustach-tool.c @@ -174,8 +174,6 @@ int main(int ac, char **av) #define MUSTACH_TOOL_JANSSON 2 #define MUSTACH_TOOL_CJSON 3 -#define TOOL MUSTACH_TOOL_JANSSON - #if TOOL == MUSTACH_TOOL_JSON_C #include "mustach-json-c.h" diff --git a/src/templating/run-original-tests.sh b/src/templating/run-original-tests.sh index 9c7d34cdd..2debca763 100755 --- a/src/templating/run-original-tests.sh +++ b/src/templating/run-original-tests.sh @@ -5,6 +5,9 @@ set -eu # even bother testing for it in configure.ac. # However, in that case, skip the test suite. +export CFLAGS="-g" + make -f Makefile.orig mustach || exit 77 -make -f Makefile.orig test +make -f Makefile.orig clean || true +make -f Makefile.orig basic-tests make -f Makefile.orig clean || true diff --git a/src/templating/templating_api.c b/src/templating/templating_api.c index 9261bde79..efe020761 100644 --- a/src/templating/templating_api.c +++ b/src/templating/templating_api.c @@ -183,7 +183,7 @@ TALER_TEMPLATING_fill (const char *tmpl, (eno = mustach_jansson_mem (tmpl, 0, /* length of tmpl */ (json_t *) root, - Mustach_With_NoExtensions, + Mustach_With_AllExtensions, (char **) result, result_size))) { diff --git a/src/templating/test1/json b/src/templating/test1/json index 5b2e3d83a..6562fb064 100644 --- a/src/templating/test1/json +++ b/src/templating/test1/json @@ -5,12 +5,12 @@ "in_ca": true, "person": false, "repo": [ - { "name": "resque", "who": [ { "committer": "joe" }, { "reviewer": "avrel" }, { "committer": "william" } ] }, - { "name": "hub", "who": [ { "committer": "jack" }, { "reviewer": "avrel" }, { "committer": "greg" } ] }, - { "name": "rip", "who": [ { "reviewer": "joe" }, { "reviewer": "jack" }, { "committer": "greg" } ] } + { "name": "resque", "who": [ { "commiter": "joe" }, { "reviewer": "avrel" }, { "commiter": "william" } ] }, + { "name": "hub", "who": [ { "commiter": "jack" }, { "reviewer": "avrel" }, { "commiter": "greg" } ] }, + { "name": "rip", "who": [ { "reviewer": "joe" }, { "reviewer": "jack" }, { "commiter": "greg" } ] } ], "person?": { "name": "Jon" }, - "special": "----{{extra}}----", + "special": "----{{extra}}----\n", "extra": 3.14159, "#sharp": "#", "!bang": "!", diff --git a/src/templating/test1/must b/src/templating/test1/must index 723f966c4..6df523669 100644 --- a/src/templating/test1/must +++ b/src/templating/test1/must @@ -12,7 +12,7 @@ Shown. {{/person}} {{#repo}} - {{name}} reviewers:{{#who}} {{reviewer}}{{/who}} committers:{{#who}} {{committer}}{{/who}} + {{name}} reviewers:{{#who}} {{reviewer}}{{/who}} commiters:{{#who}} {{commiter}}{{/who}} {{/repo}} {{#person?}} @@ -23,7 +23,7 @@ Shown. ===================================== %(%! gros commentaire %)% %(%#repo%)% - %(%name%)% reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% committers:%(%#who%)% %(%committer%)%%(%/who%)% + %(%name%)% reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% commiters:%(%#who%)% %(%commiter%)%%(%/who%)% %(%/repo%)% ===================================== %(%={{ }}=%)% diff --git a/src/templating/test1/resu.ref b/src/templating/test1/resu.ref deleted file mode 100644 index 545e58579..000000000 --- a/src/templating/test1/resu.ref +++ /dev/null @@ -1,49 +0,0 @@ -Hello Chris -You have just won 10000 dollars! - -Well, 6000 dollars, after taxes. - -Shown. - - - No person - - - - resque reviewers: avrel committers: joe william - - hub reviewers: avrel committers: jack greg - - rip reviewers: joe jack committers: greg - - - - Hi Jon! - - - -===================================== - - - resque reviewers: avrel committers: joe william - - hub reviewers: avrel committers: jack greg - - rip reviewers: joe jack committers: greg - -===================================== - -ggggggggg -----3.14159---- -jjjjjjjjj -end - -# -! -~ -~ -/ see json pointers IETF RFC 6901 -^ -= -: -> diff --git a/src/templating/test2/resu.ref b/src/templating/test2/resu.ref deleted file mode 100644 index 67d1f547d..000000000 --- a/src/templating/test2/resu.ref +++ /dev/null @@ -1,22 +0,0 @@ -

Colors

- - - - -
  • red
  • - - - - - -
  • green
  • - - - - -
  • blue
  • - - - - - diff --git a/src/templating/test3/resu.ref b/src/templating/test3/resu.ref deleted file mode 100644 index e89ce9022..000000000 --- a/src/templating/test3/resu.ref +++ /dev/null @@ -1,15 +0,0 @@ -* Chris -* 18 -* <b>GitHub & Co</b> -* GitHub & Co -* GitHub & Co - -* <b>GitHub & Co</b> -* GitHub & Co -* GitHub & Co - - -*
    • Chris
    • Kross
    -* skills:
    • JavaScript
    • PHP
    • Java
    -* age: 18 - diff --git a/src/templating/test4/resu.ref b/src/templating/test4/resu.ref deleted file mode 100644 index 2d48918ac..000000000 --- a/src/templating/test4/resu.ref +++ /dev/null @@ -1,100 +0,0 @@ -This are extensions!! - -Jon -25 - -Fred -The other Fred. - - -Hello Jon - - - - - -No Harry? Hey Calahan... - - - -Hello Fred - - - - - -Hello Fred#2 - - - - - -Hello Jon, 25 years - - -Hello Henry, 27 years - - - -Salut Amed, 24 ans - - - -Jon: /25/25 - -Henry: /27/ - -Amed: 24/24/24 - - - -Jon: /25/25 - -Henry: /27/ - -Amed: 24/24/24 - - - - (1) person: { "name": "Jon", "age": 25 } - - (2) name: Jon - - - (2) age: 25 - - - - (1) person.name: Fred - - - (1) person.name=Fred: The other Fred. - - - (1) persons: [ { "name": "Jon", "age": 25, "lang": "en" }, { "name": "Henry", "age": 27, "lang": "en" }, { "name": "Amed", "age": 24, "lang": "fr" } ] - - - (1) fellows: { "Jon": { "age": 25, "lang": "en" }, "Henry": { "age": 27, "lang": "en" }, "Amed": { "age": 24, "lang": "fr" } } - - (2) Jon: { "age": 25, "lang": "en" } - - (3) age: 25 - - (3) lang: en - - - (2) Henry: { "age": 27, "lang": "en" } - - (3) age: 27 - - (3) lang: en - - - (2) Amed: { "age": 24, "lang": "fr" } - - (3) age: 24 - - (3) lang: fr - - - diff --git a/src/templating/test5/json b/src/templating/test5/json index 5b2e3d83a..6562fb064 100644 --- a/src/templating/test5/json +++ b/src/templating/test5/json @@ -5,12 +5,12 @@ "in_ca": true, "person": false, "repo": [ - { "name": "resque", "who": [ { "committer": "joe" }, { "reviewer": "avrel" }, { "committer": "william" } ] }, - { "name": "hub", "who": [ { "committer": "jack" }, { "reviewer": "avrel" }, { "committer": "greg" } ] }, - { "name": "rip", "who": [ { "reviewer": "joe" }, { "reviewer": "jack" }, { "committer": "greg" } ] } + { "name": "resque", "who": [ { "commiter": "joe" }, { "reviewer": "avrel" }, { "commiter": "william" } ] }, + { "name": "hub", "who": [ { "commiter": "jack" }, { "reviewer": "avrel" }, { "commiter": "greg" } ] }, + { "name": "rip", "who": [ { "reviewer": "joe" }, { "reviewer": "jack" }, { "commiter": "greg" } ] } ], "person?": { "name": "Jon" }, - "special": "----{{extra}}----", + "special": "----{{extra}}----\n", "extra": 3.14159, "#sharp": "#", "!bang": "!", diff --git a/src/templating/test5/must3.mustache b/src/templating/test5/must3.mustache index 821aaac33..67eddb1ef 100644 --- a/src/templating/test5/must3.mustache +++ b/src/templating/test5/must3.mustache @@ -1,6 +1,6 @@ must3.mustache == BEGIN {{#repo}} - {{name}} reviewers:{{#who}} {{reviewer}}{{/who}} committers:{{#who}} {{committer}}{{/who}} + {{name}} reviewers:{{#who}} {{reviewer}}{{/who}} commiters:{{#who}} {{commiter}}{{/who}} {{/repo}} {{#person?}} @@ -11,7 +11,7 @@ must3.mustache == BEGIN ===================================== %(%! big comment %)% %(%#repo%)% - %(%name%)% reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% committers:%(%#who%)% %(%committer%)%%(%/who%)% + %(%name%)% reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% commiters:%(%#who%)% %(%commiter%)%%(%/who%)% %(%/repo%)% ===================================== must3.mustache == END diff --git a/src/templating/test5/resu.ref b/src/templating/test5/resu.ref deleted file mode 100644 index afc396599..000000000 --- a/src/templating/test5/resu.ref +++ /dev/null @@ -1,60 +0,0 @@ -===================================== -from json -----3.14159---- -===================================== -not found - -===================================== -without extension first -must2 == BEGIN -Hello Chris -You have just won 10000 dollars! - -Well, 6000 dollars, after taxes. - -Shown. - - - No person - -must2 == END - -===================================== -last with extension -must3.mustache == BEGIN - - resque reviewers: avrel committers: joe william - - hub reviewers: avrel committers: jack greg - - rip reviewers: joe jack committers: greg - - - - Hi Jon! - - - -===================================== - - - resque reviewers: avrel committers: joe william - - hub reviewers: avrel committers: jack greg - - rip reviewers: joe jack committers: greg - -===================================== -must3.mustache == END - -===================================== -Ensure must3 didn't change specials - - - Hi Jon! - - -%(%#person?%)% - Hi %(%name%)%! -%(%/person?%)% - diff --git a/src/templating/test5/special b/src/templating/test5/special deleted file mode 100644 index 02d9975c6..000000000 --- a/src/templating/test5/special +++ /dev/null @@ -1 +0,0 @@ -special ==SHOULD NOT BE SEEN== diff --git a/src/templating/test5/special.mustache b/src/templating/test5/special.mustache deleted file mode 100644 index 70a771fd6..000000000 --- a/src/templating/test5/special.mustache +++ /dev/null @@ -1 +0,0 @@ -special.mustache ==SHOULD NOT BE SEEN== diff --git a/src/templating/test6/.gitignore b/src/templating/test6/.gitignore index 62f4d9190..15e6dd5a5 100644 --- a/src/templating/test6/.gitignore +++ b/src/templating/test6/.gitignore @@ -1,4 +1,3 @@ resu.last vg.last test-custom-write -!test-custom-write.c diff --git a/src/templating/test6/json b/src/templating/test6/json index 5b2e3d83a..6562fb064 100644 --- a/src/templating/test6/json +++ b/src/templating/test6/json @@ -5,12 +5,12 @@ "in_ca": true, "person": false, "repo": [ - { "name": "resque", "who": [ { "committer": "joe" }, { "reviewer": "avrel" }, { "committer": "william" } ] }, - { "name": "hub", "who": [ { "committer": "jack" }, { "reviewer": "avrel" }, { "committer": "greg" } ] }, - { "name": "rip", "who": [ { "reviewer": "joe" }, { "reviewer": "jack" }, { "committer": "greg" } ] } + { "name": "resque", "who": [ { "commiter": "joe" }, { "reviewer": "avrel" }, { "commiter": "william" } ] }, + { "name": "hub", "who": [ { "commiter": "jack" }, { "reviewer": "avrel" }, { "commiter": "greg" } ] }, + { "name": "rip", "who": [ { "reviewer": "joe" }, { "reviewer": "jack" }, { "commiter": "greg" } ] } ], "person?": { "name": "Jon" }, - "special": "----{{extra}}----", + "special": "----{{extra}}----\n", "extra": 3.14159, "#sharp": "#", "!bang": "!", diff --git a/src/templating/test6/must b/src/templating/test6/must index 723f966c4..6df523669 100644 --- a/src/templating/test6/must +++ b/src/templating/test6/must @@ -12,7 +12,7 @@ Shown. {{/person}} {{#repo}} - {{name}} reviewers:{{#who}} {{reviewer}}{{/who}} committers:{{#who}} {{committer}}{{/who}} + {{name}} reviewers:{{#who}} {{reviewer}}{{/who}} commiters:{{#who}} {{commiter}}{{/who}} {{/repo}} {{#person?}} @@ -23,7 +23,7 @@ Shown. ===================================== %(%! gros commentaire %)% %(%#repo%)% - %(%name%)% reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% committers:%(%#who%)% %(%committer%)%%(%/who%)% + %(%name%)% reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% commiters:%(%#who%)% %(%commiter%)%%(%/who%)% %(%/repo%)% ===================================== %(%={{ }}=%)% diff --git a/src/templating/test6/resu.ref b/src/templating/test6/resu.ref deleted file mode 100644 index 345d3aef6..000000000 --- a/src/templating/test6/resu.ref +++ /dev/null @@ -1,147 +0,0 @@ -HELLO CHRIS -YOU HAVE JUST WON 10000 DOLLARS! - -WELL, 6000 DOLLARS, AFTER TAXES. - -SHOWN. - - - NO PERSON - - - - RESQUE REVIEWERS: AVREL COMMITTERS: JOE WILLIAM - - HUB REVIEWERS: AVREL COMMITTERS: JACK GREG - - RIP REVIEWERS: JOE JACK COMMITTERS: GREG - - - - HI JON! - - - -===================================== - - - RESQUE REVIEWERS: AVREL COMMITTERS: JOE WILLIAM - - HUB REVIEWERS: AVREL COMMITTERS: JACK GREG - - RIP REVIEWERS: JOE JACK COMMITTERS: GREG - -===================================== - -GGGGGGGGG -----3.14159---- -JJJJJJJJJ -END - -# -! -~ -~ -/ SEE JSON POINTERS IETF RFC 6901 -^ -= -: -> -hello chris -you have just won 10000 dollars! - -well, 6000 dollars, after taxes. - -shown. - - - no person - - - - resque reviewers: avrel committers: joe william - - hub reviewers: avrel committers: jack greg - - rip reviewers: joe jack committers: greg - - - - hi jon! - - - -===================================== - - - resque reviewers: avrel committers: joe william - - hub reviewers: avrel committers: jack greg - - rip reviewers: joe jack committers: greg - -===================================== - -ggggggggg -----3.14159---- -jjjjjjjjj -end - -# -! -~ -~ -/ see json pointers ietf rfc 6901 -^ -= -: -> -Hello Chris -You have just won 10000 dollars! - -Well, 6000 dollars, after taxes. - -Shown. - - - No person - - - - resque reviewers: avrel committers: joe william - - hub reviewers: avrel committers: jack greg - - rip reviewers: joe jack committers: greg - - - - Hi Jon! - - - -===================================== - - - resque reviewers: avrel committers: joe william - - hub reviewers: avrel committers: jack greg - - rip reviewers: joe jack committers: greg - -===================================== - -ggggggggg -----3.14159---- -jjjjjjjjj -end - -# -! -~ -~ -/ see json pointers IETF RFC 6901 -^ -= -: -> diff --git a/src/templating/test6/test-custom-write.c b/src/templating/test6/test-custom-write.c index cc50a47cb..4ba953a85 100644 --- a/src/templating/test6/test-custom-write.c +++ b/src/templating/test6/test-custom-write.c @@ -1,6 +1,5 @@ /* Author: José Bollo - Author: José Bollo https://gitlab.com/jobol/mustach @@ -36,7 +35,7 @@ static char *readfile(const char *filename) { int f; struct stat s; - char *result; + char *result, *ptr; size_t size, pos; ssize_t rc; @@ -80,7 +79,10 @@ static char *readfile(const char *filename) pos += (size_t)rc; if (pos > size) { size = pos + BLOCKSIZE; - result = realloc(result, size + 1); + ptr = realloc(result, size + 1); + if (!ptr) + free(result); + result = ptr; } } } while(rc > 0); @@ -132,7 +134,7 @@ int main(int ac, char **av) mode = None; else { t = readfile(*av); - s = umustach_json_c(t, o, uwrite, NULL); + s = mustach_json_c_write(t, 0, o, Mustach_With_AllExtensions, uwrite, NULL); if (s != 0) fprintf(stderr, "Template error %d\n", s); free(t); diff --git a/src/templating/test_mustach_jansson.c b/src/templating/test_mustach_jansson.c index be3db67d2..beb155f6d 100644 --- a/src/templating/test_mustach_jansson.c +++ b/src/templating/test_mustach_jansson.c @@ -24,7 +24,7 @@ */ #include "platform.h" #include "mustach-jansson.h" - +#include static void assert_template (const char *template, @@ -34,10 +34,12 @@ assert_template (const char *template, char *r; size_t sz; - GNUNET_assert (0 == mustach_jansson (template, - root, - &r, - &sz)); + GNUNET_assert (0 == mustach_jansson_mem (template, + 0, + root, + Mustach_With_AllExtensions, + &r, + &sz)); GNUNET_assert (0 == strcmp (r, expected)); GNUNET_free (r); @@ -51,7 +53,6 @@ main (int argc, json_t *root = json_object (); json_t *arr = json_array (); json_t *obj = json_object (); - json_t *contract; /* test 1 */ const char *t1 = "hello world"; const char *x1 = "hello world"; @@ -67,24 +68,15 @@ main (int argc, /* test 5 */ const char *t5 = "hello {{# v3 }}{{ y }}/{{ x }}{{ z }}{{/ v3 }}"; const char *x5 = "hello quux/baz"; - /* test 6 */ - const char *t6 = "hello {{ v2!stringify }}"; - const char *x6 = "hello [\n \"foo\",\n \"bar\"\n]"; - /* test 7 */ - const char *t7 = "amount: {{ amt!amount_decimal }} {{ amt!amount_currency }}"; - const char *x7 = "amount: 123.00 EUR"; /* test 8 */ const char *t8 = "{{^ v4 }}fallback{{/ v4 }}"; const char *x8 = "fallback"; - /* contract test 8 (contract) */ - const char *tc = "summary: {{ summary!i18n }}"; - const char *xc_en = "summary: ENGLISH"; - const char *xc_de = "summary: DEUTSCH"; - const char *xc_fr = "summary: FRANCAISE"; - (void) argc; (void) argv; + GNUNET_log_setup ("test-mustach-jansson", + "INFO", + NULL); GNUNET_assert (NULL != root); GNUNET_assert (NULL != arr); GNUNET_assert (NULL != obj); @@ -122,44 +114,12 @@ main (int argc, json_object_set_new (obj, "y", json_string ("quux"))); - contract = json_pack ("{ s:s, s:{s:s, s:s}}", - "summary", - "ENGLISH", - "summary_i18n", - "de", - "DEUTSCH", - "fr", - "FRANCAISE"); - GNUNET_assert (NULL != contract); - assert_template (t1, root, x1); assert_template (t2, root, x2); assert_template (t3, root, x3); assert_template (t4, root, x4); assert_template (t5, root, x5); - assert_template (t6, root, x6); - assert_template (t7, root, x7); assert_template (t8, root, x8); - assert_template (tc, contract, xc_en); - - GNUNET_assert (0 == - json_object_set_new (contract, - "$language", - json_string ("de"))); - assert_template (tc, contract, xc_de); - - GNUNET_assert (0 == - json_object_set_new (contract, - "$language", - json_string ("fr"))); - assert_template (tc, contract, xc_fr); - - GNUNET_assert (0 == - json_object_set_new (contract, - "$language", - json_string ("it"))); - assert_template (tc, contract, xc_en); json_decref (root); - json_decref (contract); return 0; } diff --git a/src/testing/test-taler-exchange-aggregator-postgres.conf b/src/testing/test-taler-exchange-aggregator-postgres.conf index eb641a265..7cb2fba52 100644 --- a/src/testing/test-taler-exchange-aggregator-postgres.conf +++ b/src/testing/test-taler-exchange-aggregator-postgres.conf @@ -17,9 +17,10 @@ DURATION = 14 days # Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 [exchange] +AML_THRESHOLD = EUR:1000000 + # The DB plugin to use DB = postgres diff --git a/src/testing/test-taler-exchange-wirewatch-postgres.conf b/src/testing/test-taler-exchange-wirewatch-postgres.conf index 079de5ab9..9c755c6e0 100644 --- a/src/testing/test-taler-exchange-wirewatch-postgres.conf +++ b/src/testing/test-taler-exchange-wirewatch-postgres.conf @@ -17,9 +17,10 @@ DURATION = 14 days # Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 [exchange] +AML_THRESHOLD = EUR:1000000 + # The DB plugin to use DB = postgres diff --git a/src/testing/test_auditor_api-cs.conf b/src/testing/test_auditor_api-cs.conf index 6c9a1e648..f0095e383 100644 --- a/src/testing/test_auditor_api-cs.conf +++ b/src/testing/test_auditor_api-cs.conf @@ -21,7 +21,6 @@ DURATION = 14 days # Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 [auditor] BASE_URL = "http://localhost:8083/" @@ -32,6 +31,7 @@ PUBLIC_KEY = XNYZPJJ6YPSQ4C6QPW120ACG9B5E5GBTTSYWXDMDB6G4X74TDBPG TINY_AMOUNT = EUR:0.01 [exchange] +AML_THRESHOLD = EUR:1000000 # HTTP port the exchange listens to PORT = 8081 diff --git a/src/testing/test_auditor_api-rsa.conf b/src/testing/test_auditor_api-rsa.conf index f3e66763b..dddbf0918 100644 --- a/src/testing/test_auditor_api-rsa.conf +++ b/src/testing/test_auditor_api-rsa.conf @@ -21,7 +21,6 @@ DURATION = 14 days # Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 [auditor] BASE_URL = "http://localhost:8083/" @@ -33,6 +32,7 @@ PUBLIC_KEY = XNYZPJJ6YPSQ4C6QPW120ACG9B5E5GBTTSYWXDMDB6G4X74TDBPG TINY_AMOUNT = EUR:0.01 [exchange] +AML_THRESHOLD = EUR:1000000 # HTTP port the exchange listens to PORT = 8081 diff --git a/src/testing/test_exchange_api.conf b/src/testing/test_exchange_api.conf index 2224afd91..bf73d00aa 100644 --- a/src/testing/test_exchange_api.conf +++ b/src/testing/test_exchange_api.conf @@ -20,7 +20,6 @@ DURATION = 14 days # Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 [auditor] BASE_URL = "http://localhost:8083/" @@ -35,8 +34,8 @@ LOGIC = oauth2 USER_TYPE = INDIVIDUAL PROVIDED_CHECKS = DUMMY KYC_OAUTH2_VALIDITY = forever -KYC_OAUTH2_AUTH_URL = http://localhost:6666/oauth/v2/token -KYC_OAUTH2_LOGIN_URL = http://localhost:6666/oauth/v2/login +KYC_OAUTH2_TOKEN_URL = http://localhost:6666/oauth/v2/token +KYC_OAUTH2_AUTHORIZE_URL = http://localhost:6666/oauth/v2/login KYC_OAUTH2_INFO_URL = http://localhost:6666/api/user/me KYC_OAUTH2_CLIENT_ID = taler-exchange KYC_OAUTH2_CLIENT_SECRET = exchange-secret @@ -54,6 +53,8 @@ TIMEFRAME = 1d TERMS_ETAG = 0 PRIVACY_ETAG = 0 +AML_THRESHOLD = EUR:1000000 + # HTTP port the exchange listens to PORT = 8081 diff --git a/src/testing/test_exchange_api_keys_cherry_picking.conf b/src/testing/test_exchange_api_keys_cherry_picking.conf index 55e0c625f..5637bb66f 100644 --- a/src/testing/test_exchange_api_keys_cherry_picking.conf +++ b/src/testing/test_exchange_api_keys_cherry_picking.conf @@ -17,7 +17,6 @@ TALER_CACHE_HOME = $TALER_HOME/.cache/taler/ [taler] # Currency supported by the exchange (can only be one) CURRENCY = EUR -AML_THRESHOLD = EUR:1000000 [taler-exchange-secmod-eddsa] OVERLAP_DURATION = 1 s @@ -32,6 +31,9 @@ BASE_URL = "http://localhost:8083/" PORT = 8083 [exchange] + +AML_THRESHOLD = EUR:1000000 + # HTTP port the exchange listens to PORT = 8081 diff --git a/src/testing/test_kyc_api.conf b/src/testing/test_kyc_api.conf index 9c0b43635..abc7f3e4e 100644 --- a/src/testing/test_kyc_api.conf +++ b/src/testing/test_kyc_api.conf @@ -21,7 +21,6 @@ DURATION = 14 days # Currency supported by the exchange (can only be one) CURRENCY = EUR CURRENCY_ROUND_UNIT = EUR:0.01 -AML_THRESHOLD = EUR:1000000 [auditor] BASE_URL = "http://localhost:8083/" @@ -32,6 +31,7 @@ PORT = 8083 TINY_AMOUNT = EUR:0.01 [exchange] +AML_THRESHOLD = EUR:1000000 # HTTP port the exchange listens to PORT = 8081 @@ -53,8 +53,8 @@ LOGIC = oauth2 USER_TYPE = INDIVIDUAL PROVIDED_CHECKS = DUMMY KYC_OAUTH2_VALIDITY = forever -KYC_OAUTH2_AUTH_URL = http://localhost:6666/oauth/v2/token -KYC_OAUTH2_LOGIN_URL = http://localhost:6666/oauth/v2/login +KYC_OAUTH2_TOKEN_URL = http://localhost:6666/oauth/v2/token +KYC_OAUTH2_AUTHORIZE_URL = http://localhost:6666/oauth/v2/login KYC_OAUTH2_INFO_URL = http://localhost:6666/api/user/me KYC_OAUTH2_CLIENT_ID = taler-exchange KYC_OAUTH2_CLIENT_SECRET = exchange-secret diff --git a/src/testing/testing_api_cmd_nexus_fetch_transactions.c b/src/testing/testing_api_cmd_nexus_fetch_transactions.c index ff1497f3f..fc59444de 100644 --- a/src/testing/testing_api_cmd_nexus_fetch_transactions.c +++ b/src/testing/testing_api_cmd_nexus_fetch_transactions.c @@ -81,6 +81,7 @@ nft_run (void *cls, "--header=Content-Type:application/json", "--auth-no-challenge", "--output-file=/dev/null", + "--output-document=/dev/null", "--post-data={\"level\":\"all\",\"rangeType\":\"latest\"}", user, pass, diff --git a/src/util/conversion.c b/src/util/conversion.c index 501ca0145..fdeffba40 100644 --- a/src/util/conversion.c +++ b/src/util/conversion.c @@ -251,7 +251,7 @@ child_done_cb (void *cls, long unsigned int exit_code) { struct TALER_JSON_ExternalConversion *ec = cls; - json_t *j; + json_t *j = NULL; json_error_t err; ec->cwh = NULL; @@ -269,16 +269,19 @@ child_done_cb (void *cls, } GNUNET_OS_process_destroy (ec->helper); ec->helper = NULL; - j = json_loadb (ec->read_buf, - ec->read_pos, - JSON_REJECT_DUPLICATES, - &err); - if (NULL == j) + if (0 != ec->read_pos) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to parse JSON from helper at %d: %s\n", - err.position, - err.text); + j = json_loadb (ec->read_buf, + ec->read_pos, + JSON_REJECT_DUPLICATES, + &err); + if (NULL == j) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to parse JSON from helper at %d: %s\n", + err.position, + err.text); + } } ec->cb (ec->cb_cls, type, diff --git a/src/util/test_amount.c b/src/util/test_amount.c index c94f24fe5..a45b71de7 100644 --- a/src/util/test_amount.c +++ b/src/util/test_amount.c @@ -85,8 +85,8 @@ main (int argc, GNUNET_assert (0 == a2.value); GNUNET_assert (TALER_AMOUNT_FRAC_BASE / 100 * 2 == a2.fraction); c = TALER_amount_to_string (&a2); - GNUNET_assert (0 == strcmp ("eur:0.02", - c)); + GNUNET_assert (0 == strcasecmp ("eur:0.02", + c)); GNUNET_free (c); /* test conversion with leading space and with fraction */ diff --git a/src/util/url.c b/src/util/url.c index a140a3a2e..1ac197551 100644 --- a/src/util/url.c +++ b/src/util/url.c @@ -322,7 +322,7 @@ TALER_url_valid_charset (const char *url) for (unsigned int i = 0; '\0' != url[i]; i++) { #define ALLOWED_CHARACTERS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:;&?-.,=_~%+" + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:;&?-.,=_~%+#" if (NULL == strchr (ALLOWED_CHARACTERS, (int) url[i])) return false;