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
-
-
-*
-* skills:
-* 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;