diff options
| author | Özgür Kesim <oec-taler@kesim.org> | 2023-06-03 10:45:31 +0200 | 
|---|---|---|
| committer | Özgür Kesim <oec-taler@kesim.org> | 2023-06-03 10:45:31 +0200 | 
| commit | 80a1b8f5240e8307e1c73862fb8810d7c2bdc874 (patch) | |
| tree | 5661068ec9976aa8711dc931355f6bd87ab35fd6 | |
| parent | 4a31a180a4595aa040060e91ccde4f839aa02f72 (diff) | |
| parent | 2ea3ae1008020589b43a51663c45556a08547212 (diff) | |
Merge branch 'master' into age-withdraw
117 files changed, 2291 insertions, 1167 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am index 99927e7e..9d1fe6cf 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 -Subproject 85736484cb0da26aded705ebb1e944e8bb1b850 +Subproject f9ea79a6aae074928f44960f69bc3c2d0c9c7e1 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 00000000..60296ead --- /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 00000000..1c86ccc3 --- /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 00000000..f68c5955 --- /dev/null +++ b/contrib/packages/fedora/etc-taler-auditor/apache2/sites-available/taler-auditor.conf @@ -0,0 +1,4 @@ +<Location "/taler-auditor/"> +ProxyPass "unix:/var/lib/taler-auditor/auditor.sock|http://example.com/" +RequestHeader add "X-Forwarded-Proto" "https" +</Location> 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 00000000..f74035d5 --- /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 00000000..3d3aef33 --- /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 00000000..b81bb817 --- /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 00000000..3ec14feb --- /dev/null +++ b/contrib/packages/fedora/etc-taler-exchange/apache2/sites-available/taler-exchange.conf @@ -0,0 +1,4 @@ +<Location "/taler-exchange/"> +ProxyPass "unix:/run/taler/exchange-httpd/exchange-http.sock|http://example.com/" +RequestHeader add "X-Forwarded-Proto" "https" +</Location> 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 00000000..9b61a32d --- /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 00000000..d5938f2b --- /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 00000000..8294525c --- /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 00000000..4ad7e06f --- /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 00000000..8c8d1432 --- /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 00000000..a7a727b6 --- /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 00000000..9aefab64 --- /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 00000000..246cad5c --- /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 00000000..bfc44a9a --- /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 00000000..97a385c1 --- /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 00000000..250f210f --- /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 00000000..3671bdc7 --- /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 00000000..e0246899 --- /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 00000000..3b5e0745 --- /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 00000000..e8fba173 --- /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 00000000..10a9585a --- /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 00000000..e26af20d --- /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 00000000..7b74737b --- /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 00000000..85bb9268 --- /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 853c8125..c10bc267 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 89583d5b..d5938f2b 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 75c670f7..4ad7e06f 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 0b8cd63d..6475ea41 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 -Subproject 8452f991dd967328207fab52a99beb19e2cb4df +Subproject 66e99d09d4351bb6e6c5fd442f14ec7cf1363a8 diff --git a/src/auditor/taler-auditor-exchange.c b/src/auditor/taler-auditor-exchange.c index 04181ce3..b34d1484 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 d0d14b8d..79721be1 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 7b5b0d1f..5e44781d 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 fd755381..8e61129f 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 80765dd9..b1aababc 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 3d87a2a5..ba74a10f 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 38e5816f..d1558a49 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 38110a5e..0073d85e 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 7e11655c..348967f7 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -154,6 +154,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   * an AML check? @@ -1845,6 +1855,17 @@ exchange_serve_process_config (void)      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 5ab0ea92..24e08772 100644 --- a/src/exchange/taler-exchange-httpd.h +++ b/src/exchange/taler-exchange-httpd.h @@ -65,6 +65,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.   */  extern char *TEH_revocation_directory; @@ -98,6 +103,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   * an AML check? diff --git a/src/exchange/taler-exchange-httpd_aml-decision-get.c b/src/exchange/taler-exchange-httpd_aml-decision-get.c index 6b36fe27..b4f337db 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 00000000..ef917a55 --- /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 <http://www.gnu.org/licenses/> +*/ +/** + * @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 00000000..57276604 --- /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 <http://www.gnu.org/licenses/> +*/ +/** + * @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 <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_json_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#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 fa87a3b8..b39093ec 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 9668ee54..b3175bd2 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 <gnunet/gnunet_json_lib.h>  #include <jansson.h>  #include <microhttpd.h> -#include <pthread.h>  #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" @@ -69,6 +69,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.     */ @@ -160,6 +165,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.   *   * Note that the "decref" for the @a response @@ -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) -    { -      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, -                                            "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) +    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, -                                            "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 f8fe711d..415e5de9 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" @@ -54,6 +55,11 @@ struct KycWebhookContext    struct TEH_RequestContext *rc;    /** +   * Handle for the KYC-AML trigger interaction. +   */ +  struct TEH_KycAmlTrigger *kat; + +  /**     * Plugin responsible for the webhook.     */    struct TALER_KYCLOGIC_Plugin *plugin; @@ -141,6 +147,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.   *   * Note that the "decref" for the @a response @@ -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 297d8cee..d0f3614e 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 b53a8641..ae983682 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 00000000..9baa32ba --- /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 0af23b9d..80cf6230 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 03d17aee..df5b6c3d 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 00000000..e45d46b3 --- /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 <http://www.gnu.org/licenses/> +-- + +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 ee78b87f..4df9cb40 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 00979e19..02bdf017 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 00000000..f1959a66 --- /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 <http://www.gnu.org/licenses/> +-- + +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 dffcd8b5..cf57d8e8 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 fd90950f..361f491e 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 (provider_account_id), +    (NULL == provider_legitimization_id)      ? GNUNET_PQ_query_param_null () -    : GNUNET_PQ_query_param_string (birthdate), +    : 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 8ee307d7..c1aad0eb 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 e16a4b74..9baaf3b1 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 534e9a1d..3fcad58c 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 691c57d3..72fde749 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; i<num_results; i++) +  { +    struct GNUNET_PQ_ResultSpec rs[] = { +      GNUNET_PQ_result_spec_bool ( +        "transaction_duplicate", +        &ctx->transaction_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; i<reserves_length; i++) +  { +    const struct TALER_EXCHANGEDB_ReserveInInfo *reserve = &reserves[i]; + +    TALER_payto_hash (reserve->sender_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; i<reserves_length; i++) +  { +    results[i] = transaction_duplicates[i] +      ? GNUNET_DB_STATUS_SUCCESS_NO_RESULTS +      : GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +  } + +  if (! need_update) +  { +    qs = reserves_length; +    goto finished; +  } +  if (GNUNET_OK != +      TEH_PG_start (pg, +                    "reserve-insert-continued")) +  { +    GNUNET_break (0); +    qs = GNUNET_DB_STATUS_HARD_ERROR; +    goto finished; +  } + +  for (unsigned int i = 0; i<reserves_length; i++) +  { +    if (! conflicts[i]) +      continue; +    { +      bool duplicate; +      struct GNUNET_PQ_QueryParam params[] = { +        GNUNET_PQ_query_param_auto_from_type (reserve_pubs[i]), +        GNUNET_PQ_query_param_timestamp (&reserve_expiration), +        GNUNET_PQ_query_param_uint64 (&wire_reference[i]), +        TALER_PQ_query_param_amount (balances[i]), +        GNUNET_PQ_query_param_string (exchange_account_names[i]), +        GNUNET_PQ_query_param_auto_from_type (h_paytos[i]), +        GNUNET_PQ_query_param_string (notify_s[i]), +        GNUNET_PQ_query_param_end +      }; +      struct GNUNET_PQ_ResultSpec rs[] = { +        GNUNET_PQ_result_spec_bool ("duplicate", +                                    &duplicate), +        GNUNET_PQ_result_spec_end +      }; +      enum GNUNET_DB_QueryStatus qs; + +      qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, +                                                     "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; i<reserves_length; i++) +    GNUNET_free (rrs[i].notify_s); +  return qs; +} + + +#endif diff --git a/src/exchangedb/pg_select_kyc_attributes.c b/src/exchangedb/pg_select_kyc_attributes.c index c9b992da..99ac43b3 100644 --- a/src/exchangedb/pg_select_kyc_attributes.c +++ b/src/exchangedb/pg_select_kyc_attributes.c @@ -80,14 +80,9 @@ 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_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", @@ -110,7 +105,6 @@ get_attributes_cb (void *cls,      ctx->cb (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 a07f2a14..342f9ef3 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 f77eb2bf..00000000 --- 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 <http://www.gnu.org/licenses/> - */ -/** - * @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 5d17eb7f..00000000 --- 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 <http://www.gnu.org/licenses/> - */ -/** - * @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 4ae44ce5..711f4780 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 b3ebc754..00648419 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 12ec3656..c9277ea6 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 04aa0004..c9a35a2a 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 3769315e..6d4ca109 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 3a6ba651..92fb36fb 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, -    struct GNUNET_TIME_Timestamp collection_time, -    struct GNUNET_TIME_Timestamp 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, +    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);    /** diff --git a/src/kyclogic/Makefile.am b/src/kyclogic/Makefile.am index 20430a4e..75ea13b6 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 0e1fe96e..753fb689 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 6f83c0e4..61b38367 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 3273a51f..95dc4bb7 100644 --- a/src/kyclogic/plugin_kyclogic_kycaid.c +++ b/src/kyclogic/plugin_kyclogic_kycaid.c @@ -88,6 +88,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.     */    struct GNUNET_TIME_Relative validity; @@ -216,6 +222,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.     */    const struct TALER_KYCLOGIC_ProviderDetails *pd; @@ -226,6 +238,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.     */    char *verification_id; @@ -262,6 +279,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.     */    unsigned int response_code; @@ -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); @@ -751,6 +796,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,253 +904,30 @@ 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)) -      { -        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_string_value ( +        json_object_get ( +          j, +          "profile_status")); +      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;          ks = (0 == strcasecmp ("pending",                                 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 c72b04b7..e7350f01 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 Challenger ``/setup`` endpoint for +   * approving address validations. NULL if not used. +   */ +  char *setup_url; + +  /**     * URL of the OAuth2.0 endpoint for KYC checks. -   * (token/auth)     */ -  char *auth_url; +  char *authorize_url;    /**     * URL of the OAuth2.0 endpoint for KYC checks. +   * (token/auth)     */ -  char *login_url; +  char *token_url;    /**     * URL of the user info access endpoint. @@ -148,6 +154,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.     */    TALER_KYCLOGIC_InitiateCallback cb; @@ -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, @@ -538,6 +574,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.   *   * @param cls the @e cls of this struct with the plugin-specific state @@ -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 00000000..96aca2b8 --- /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 0e804630..601b163d 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", @@ -750,6 +751,10 @@ decode_keys_json (const json_t *resp_obj,      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),        NULL), @@ -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 0e9f2e08..31ec3e47 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 d4b2d060..711e6381 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 6b5234db..f960bdcc 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 3a1d2073..e902b2c7 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 6699b0e8..7034ef99 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 0c8f4407..83a0813e 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 9c7d34cd..2debca76 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 9261bde7..efe02076 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 5b2e3d83..6562fb06 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 723f966c..6df52366 100644 --- a/src/templating/test1/must +++ b/src/templating/test1/must @@ -12,7 +12,7 @@ Shown.  {{/person}}  {{#repo}} -  <b>{{name}}</b> reviewers:{{#who}} {{reviewer}}{{/who}} committers:{{#who}} {{committer}}{{/who}} +  <b>{{name}}</b> reviewers:{{#who}} {{reviewer}}{{/who}} commiters:{{#who}} {{commiter}}{{/who}}  {{/repo}}  {{#person?}} @@ -23,7 +23,7 @@ Shown.  =====================================  %(%! gros commentaire %)%  %(%#repo%)% -  <b>%(%name%)%</b> reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% committers:%(%#who%)% %(%committer%)%%(%/who%)% +  <b>%(%name%)%</b> 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 545e5857..00000000 --- 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 - - - -  <b>resque</b> reviewers:  avrel  committers: joe  william - -  <b>hub</b> reviewers:  avrel  committers: jack  greg - -  <b>rip</b> reviewers: joe jack  committers:   greg - - - -  Hi Jon! - - - -===================================== - - -  <b>resque</b> reviewers:  avrel  committers: joe  william - -  <b>hub</b> reviewers:  avrel  committers: jack  greg - -  <b>rip</b> 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 67d1f547..00000000 --- a/src/templating/test2/resu.ref +++ /dev/null @@ -1,22 +0,0 @@ -<h1>Colors</h1> - - - -   -    <li><strong>red</strong></li> -   -   - -   -   -    <li><a href="#Green">green</a></li> -   - -   -   -    <li><a href="#Blue">blue</a></li> -   - - - - diff --git a/src/templating/test3/resu.ref b/src/templating/test3/resu.ref deleted file mode 100644 index e89ce902..00000000 --- a/src/templating/test3/resu.ref +++ /dev/null @@ -1,15 +0,0 @@ -* Chris -* 18 -* <b>GitHub & Co</b> -* <b>GitHub & Co</b> -* <b>GitHub & Co</b> - -* <b>GitHub & Co</b> -* <b>GitHub & Co</b> -* <b>GitHub & Co</b> - - -* <ul><li>Chris</li><li>Kross</li></ul> -* skills: <ul><li>JavaScript</li><li>PHP</li><li>Java</li></ul> -* age: 18 - diff --git a/src/templating/test4/resu.ref b/src/templating/test4/resu.ref deleted file mode 100644 index 2d48918a..00000000 --- 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 5b2e3d83..6562fb06 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 821aaac3..67eddb1e 100644 --- a/src/templating/test5/must3.mustache +++ b/src/templating/test5/must3.mustache @@ -1,6 +1,6 @@  must3.mustache == BEGIN  {{#repo}} -  <b>{{name}}</b> reviewers:{{#who}} {{reviewer}}{{/who}} committers:{{#who}} {{committer}}{{/who}} +  <b>{{name}}</b> reviewers:{{#who}} {{reviewer}}{{/who}} commiters:{{#who}} {{commiter}}{{/who}}  {{/repo}}  {{#person?}} @@ -11,7 +11,7 @@ must3.mustache == BEGIN  =====================================  %(%! big comment %)%  %(%#repo%)% -  <b>%(%name%)%</b> reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% committers:%(%#who%)% %(%committer%)%%(%/who%)% +  <b>%(%name%)%</b> 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 afc39659..00000000 --- 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 - -  <b>resque</b> reviewers:  avrel  committers: joe  william - -  <b>hub</b> reviewers:  avrel  committers: jack  greg - -  <b>rip</b> reviewers: joe jack  committers:   greg - - - -  Hi Jon! - - - -===================================== - - -  <b>resque</b> reviewers:  avrel  committers: joe  william - -  <b>hub</b> reviewers:  avrel  committers: jack  greg - -  <b>rip</b> 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 02d9975c..00000000 --- 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 70a771fd..00000000 --- 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 62f4d919..15e6dd5a 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 5b2e3d83..6562fb06 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 723f966c..6df52366 100644 --- a/src/templating/test6/must +++ b/src/templating/test6/must @@ -12,7 +12,7 @@ Shown.  {{/person}}  {{#repo}} -  <b>{{name}}</b> reviewers:{{#who}} {{reviewer}}{{/who}} committers:{{#who}} {{committer}}{{/who}} +  <b>{{name}}</b> reviewers:{{#who}} {{reviewer}}{{/who}} commiters:{{#who}} {{commiter}}{{/who}}  {{/repo}}  {{#person?}} @@ -23,7 +23,7 @@ Shown.  =====================================  %(%! gros commentaire %)%  %(%#repo%)% -  <b>%(%name%)%</b> reviewers:%(%#who%)% %(%reviewer%)%%(%/who%)% committers:%(%#who%)% %(%committer%)%%(%/who%)% +  <b>%(%name%)%</b> 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 345d3aef..00000000 --- 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 - - - -  <B>RESQUE</B> REVIEWERS:  AVREL  COMMITTERS: JOE  WILLIAM - -  <B>HUB</B> REVIEWERS:  AVREL  COMMITTERS: JACK  GREG - -  <B>RIP</B> REVIEWERS: JOE JACK  COMMITTERS:   GREG - - - -  HI JON! - - - -===================================== - - -  <B>RESQUE</B> REVIEWERS:  AVREL  COMMITTERS: JOE  WILLIAM - -  <B>HUB</B> REVIEWERS:  AVREL  COMMITTERS: JACK  GREG - -  <B>RIP</B> 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 - - - -  <b>resque</b> reviewers:  avrel  committers: joe  william - -  <b>hub</b> reviewers:  avrel  committers: jack  greg - -  <b>rip</b> reviewers: joe jack  committers:   greg - - - -  hi jon! - - - -===================================== - - -  <b>resque</b> reviewers:  avrel  committers: joe  william - -  <b>hub</b> reviewers:  avrel  committers: jack  greg - -  <b>rip</b> 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 - - - -  <b>resque</b> reviewers:  avrel  committers: joe  william - -  <b>hub</b> reviewers:  avrel  committers: jack  greg - -  <b>rip</b> reviewers: joe jack  committers:   greg - - - -  Hi Jon! - - - -===================================== - - -  <b>resque</b> reviewers:  avrel  committers: joe  william - -  <b>hub</b> reviewers:  avrel  committers: jack  greg - -  <b>rip</b> 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 cc50a47c..4ba953a8 100644 --- a/src/templating/test6/test-custom-write.c +++ b/src/templating/test6/test-custom-write.c @@ -1,6 +1,5 @@  /*   Author: José Bollo <jobol@nonadev.net> - Author: José Bollo <jose.bollo@iot.bzh>   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 be3db67d..beb155f6 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 <gnunet/gnunet_util_lib.h>  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 eb641a26..7cb2fba5 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 079de5ab..9c755c6e 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 6c9a1e64..f0095e38 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 f3e66763..dddbf091 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 2224afd9..bf73d00a 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 55e0c625..5637bb66 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 9c0b4363..abc7f3e4 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 ff1497f3..fc59444d 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 501ca014..fdeffba4 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 c94f24fe..a45b71de 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 a140a3a2..1ac19755 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;  | 
