Compare commits
33 Commits
4a31a180a4
...
fb5bc18c58
Author | SHA1 | Date | |
---|---|---|---|
fb5bc18c58 | |||
80a1b8f524 | |||
2ea3ae1008 | |||
|
04885a289a | ||
|
be40886515 | ||
|
ae6e62a0a3 | ||
|
89e2a02380 | ||
|
d36f1b3b6a | ||
|
aa8d44aab3 | ||
|
721cd047ff | ||
|
144b3a50a9 | ||
|
689fd46a60 | ||
|
bac7123763 | ||
|
9f081d28d7 | ||
|
0045eea277 | ||
|
6cc3846f4d | ||
|
b30952ed72 | ||
|
79671bba66 | ||
|
f7dc35e59e | ||
|
755955de28 | ||
|
e371d76cfe | ||
|
30b953ff0f | ||
|
29694be4b1 | ||
|
ef2059c9df | ||
|
4e5a9906c2 | ||
|
89ed38c03c | ||
|
40e45e7f5e | ||
|
bac4932cf5 | ||
|
b7d1ca4cd9 | ||
|
89e21002d6 | ||
|
90ca90b576 | ||
|
92df1cb1da | ||
|
c9ed524bc3 |
@ -274,11 +274,11 @@ AS_CASE([$with_gnunet],
|
||||
CPPFLAGS="-I$with_gnunet/include ${CPPFLAGS}"])
|
||||
CPPFLAGS="${CPPFLAGS} ${POSTGRESQL_CPPFLAGS}"
|
||||
AC_CHECK_HEADERS([gnunet/gnunet_pq_lib.h],
|
||||
[AC_CHECK_LIB([gnunetpq], [GNUNET_PQ_result_spec_string], libgnunetpq=1)])
|
||||
[AC_CHECK_LIB([gnunetpq], [GNUNET_PQ_result_spec_array_string], libgnunetpq=1)])
|
||||
AS_IF([test $libgnunetpq != 1],
|
||||
[AC_MSG_ERROR([[
|
||||
***
|
||||
*** You need libgnunetpq to build this program.
|
||||
*** You need libgnunetpq version >= 4.0.0 to build this program.
|
||||
*** Make sure you have Postgres installed while
|
||||
*** building GNUnet (and that your GNUnet version
|
||||
*** is recent!)
|
||||
|
@ -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.
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 85736484cb0da26aded705ebb1e944e8bb1b8504
|
||||
Subproject commit f9ea79a6aae074928f44960f69bc3c2d0c9c7e1d
|
@ -0,0 +1 @@
|
||||
# This configuration will be changed by tooling. Do not touch it manually.
|
@ -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
|
@ -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>
|
@ -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";
|
||||
}
|
||||
}
|
@ -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
|
@ -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.
|
@ -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>
|
@ -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";
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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.
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
@ -8,3 +8,6 @@
|
||||
|
||||
# Only supported database is Postgres right now.
|
||||
DATABASE = postgres
|
||||
|
||||
|
||||
|
||||
|
@ -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\
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 8452f991dd967328207fab52a99beb19e2cb4dff
|
||||
Subproject commit 66e99d09d4351bb6e6c5fd442f14ec7cf1363a81
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to run `taler-exchange-wire`, is your PATH correct?\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 ==
|
||||
if (GNUNET_OK !=
|
||||
TALER_TESTING_prepare_exchange (cfg_filename,
|
||||
(GNUNET_YES == use_fakebank)
|
||||
? GNUNET_YES
|
||||
: GNUNET_NO,
|
||||
&ec));
|
||||
&ec))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to prepare the exchange for launch\n");
|
||||
GNUNET_free (cfg_filename);
|
||||
return BAD_CONFIG_FILE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -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],
|
||||
(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_OK !=
|
||||
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;
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -153,6 +153,16 @@ struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
|
||||
*/
|
||||
char *TEH_currency;
|
||||
|
||||
/**
|
||||
* Name of the KYC-AML-trigger evaluation binary.
|
||||
*/
|
||||
char *TEH_kyc_aml_trigger;
|
||||
|
||||
/**
|
||||
* Option set to #GNUNET_YES if tipping is enabled.
|
||||
*/
|
||||
int TEH_enable_tipping;
|
||||
|
||||
/**
|
||||
* What is the largest amount we allow a peer to
|
||||
* merge into a reserve before always triggering
|
||||
@ -1844,6 +1854,17 @@ exchange_serve_process_config (void)
|
||||
"valid relative time expected");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
|
||||
"exchange",
|
||||
"KYC_AML_TRIGGER",
|
||||
&TEH_kyc_aml_trigger))
|
||||
{
|
||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||
"exchange",
|
||||
"KYC_AML_TRIGGER");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TALER_config_get_currency (TEH_cfg,
|
||||
&TEH_currency))
|
||||
@ -1855,19 +1876,30 @@ exchange_serve_process_config (void)
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TALER_config_get_amount (TEH_cfg,
|
||||
"taler",
|
||||
"exchange",
|
||||
"AML_THRESHOLD",
|
||||
&TEH_aml_threshold))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Need amount in section `TALER' under `AML_THRESHOLD'\n");
|
||||
"Need amount in section `exchange' under `AML_THRESHOLD'\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (0 != strcmp (TEH_currency,
|
||||
TEH_aml_threshold.currency))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Amount in section `TALER' under `AML_THRESHOLD' uses the wrong currency!\n");
|
||||
"Amount in section `exchange' under `AML_THRESHOLD' uses the wrong currency!\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
TEH_enable_tipping
|
||||
= GNUNET_CONFIGURATION_get_value_yesno (
|
||||
TEH_cfg,
|
||||
"exchange",
|
||||
"ENABLE_TIPPING");
|
||||
if (GNUNET_SYSERR == TEH_enable_tipping)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Need YES or NO in section `exchange' under `ENABLE_TIPPING'\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
|
@ -64,6 +64,11 @@ extern int TEH_check_invariants_flag;
|
||||
*/
|
||||
extern int TEH_allow_keys_timetravel;
|
||||
|
||||
/**
|
||||
* Option set to #GNUNET_YES if tipping is enabled.
|
||||
*/
|
||||
extern int TEH_enable_tipping;
|
||||
|
||||
/**
|
||||
* Main directory with revocation data.
|
||||
*/
|
||||
@ -97,6 +102,11 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
|
||||
*/
|
||||
extern char *TEH_currency;
|
||||
|
||||
/**
|
||||
* Name of the KYC-AML-trigger evaluation binary.
|
||||
*/
|
||||
extern char *TEH_kyc_aml_trigger;
|
||||
|
||||
/**
|
||||
* What is the largest amount we allow a peer to
|
||||
* merge into a reserve before always triggering
|
||||
|
@ -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,
|
||||
|
239
src/exchange/taler-exchange-httpd_common_kyc.c
Normal file
239
src/exchange/taler-exchange-httpd_common_kyc.c
Normal file
@ -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);
|
||||
}
|
99
src/exchange/taler-exchange-httpd_common_kyc.h
Normal file
99
src/exchange/taler-exchange-httpd_common_kyc.h
Normal file
@ -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
|
@ -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",
|
||||
|
@ -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"
|
||||
|
||||
@ -68,6 +68,11 @@ struct KycProofContext
|
||||
*/
|
||||
struct TALER_KYCLOGIC_ProofHandle *ph;
|
||||
|
||||
/**
|
||||
* KYC AML trigger operation.
|
||||
*/
|
||||
struct TEH_KycAmlTrigger *kat;
|
||||
|
||||
/**
|
||||
* Process information about the user for the plugin from the database, can
|
||||
* be NULL.
|
||||
@ -159,6 +164,28 @@ TEH_kyc_proof_cleanup (void)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called after the KYC-AML trigger is done.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param http_status final HTTP status to return
|
||||
* @param[in] response final HTTP ro return
|
||||
*/
|
||||
static void
|
||||
proof_finish (
|
||||
void *cls,
|
||||
unsigned int http_status,
|
||||
struct MHD_Response *response)
|
||||
{
|
||||
struct KycProofContext *kpc = cls;
|
||||
|
||||
kpc->kat = NULL;
|
||||
kpc->response_code = http_status;
|
||||
kpc->response = response;
|
||||
kpc_resume (kpc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called with the result of a proof check operation.
|
||||
*
|
||||
@ -192,74 +219,40 @@ proof_cb (
|
||||
kpc->ph = NULL;
|
||||
GNUNET_async_scope_enter (&rc->async_scope_id,
|
||||
&old_scope);
|
||||
|
||||
if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
|
||||
{
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
size_t eas;
|
||||
void *ea;
|
||||
const char *birthdate;
|
||||
struct GNUNET_ShortHashCode kyc_prox;
|
||||
|
||||
TALER_CRYPTO_attributes_to_kyc_prox (attributes,
|
||||
&kyc_prox);
|
||||
birthdate = json_string_value (json_object_get (attributes,
|
||||
TALER_ATTRIBUTE_BIRTHDATE));
|
||||
TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key,
|
||||
attributes,
|
||||
&ea,
|
||||
&eas);
|
||||
qs = TEH_plugin->insert_kyc_attributes (
|
||||
TEH_plugin->cls,
|
||||
&kpc->h_payto,
|
||||
&kyc_prox,
|
||||
kpc->provider_section,
|
||||
birthdate,
|
||||
GNUNET_TIME_timestamp_get (),
|
||||
GNUNET_TIME_absolute_to_timestamp (expiration),
|
||||
eas,
|
||||
ea);
|
||||
GNUNET_free (ea);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
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->kat = TEH_kyc_finished (&rc->async_scope_id,
|
||||
kpc->process_row,
|
||||
kpc->provider_section,
|
||||
&kpc->h_payto,
|
||||
kpc->provider_section,
|
||||
provider_user_id,
|
||||
provider_legitimization_id,
|
||||
expiration);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
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);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2022 Taler Systems SA
|
||||
Copyright (C) 2022-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -28,6 +28,7 @@
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler-exchange-httpd_common_kyc.h"
|
||||
#include "taler-exchange-httpd_kyc-webhook.h"
|
||||
#include "taler-exchange-httpd_responses.h"
|
||||
|
||||
@ -53,6 +54,11 @@ struct KycWebhookContext
|
||||
*/
|
||||
struct TEH_RequestContext *rc;
|
||||
|
||||
/**
|
||||
* Handle for the KYC-AML trigger interaction.
|
||||
*/
|
||||
struct TEH_KycAmlTrigger *kat;
|
||||
|
||||
/**
|
||||
* Plugin responsible for the webhook.
|
||||
*/
|
||||
@ -140,6 +146,28 @@ TEH_kyc_webhook_cleanup (void)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called after the KYC-AML trigger is done.
|
||||
*
|
||||
* @param cls closure with a `struct KycWebhookContext *`
|
||||
* @param http_status final HTTP status to return
|
||||
* @param[in] response final HTTP ro return
|
||||
*/
|
||||
static void
|
||||
kyc_aml_webhook_finished (
|
||||
void *cls,
|
||||
unsigned int http_status,
|
||||
struct MHD_Response *response)
|
||||
{
|
||||
struct KycWebhookContext *kwh = cls;
|
||||
|
||||
kwh->kat = NULL;
|
||||
kwh->response = response;
|
||||
kwh->response_code = http_status;
|
||||
kwh_resume (kwh);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called with the result of a KYC webhook operation.
|
||||
*
|
||||
@ -178,58 +206,27 @@ webhook_finished_cb (
|
||||
switch (status)
|
||||
{
|
||||
case TALER_KYCLOGIC_STATUS_SUCCESS:
|
||||
/* _successfully_ resumed case */
|
||||
{
|
||||
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,
|
||||
kwh->kat = TEH_kyc_finished (
|
||||
&kwh->rc->async_scope_id,
|
||||
process_row,
|
||||
provider_section,
|
||||
account_id,
|
||||
provider_section,
|
||||
provider_user_id,
|
||||
provider_legitimization_id,
|
||||
expiration);
|
||||
if (qs < 0)
|
||||
expiration,
|
||||
attributes,
|
||||
http_status,
|
||||
response,
|
||||
&kyc_aml_webhook_finished,
|
||||
kwh);
|
||||
if (NULL == kwh->kat)
|
||||
{
|
||||
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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
7
src/exchange/taler-exchange-kyc-aml-pep-trigger.sh
Executable file
7
src/exchange/taler-exchange-kyc-aml-pep-trigger.sh
Executable file
@ -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 -)
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
$$;
|
||||
|
||||
|
44
src/exchangedb/0004-kyc_attributes.sql
Normal file
44
src/exchangedb/0004-kyc_attributes.sql
Normal file
@ -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);
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
92
src/exchangedb/exchange_do_insert_kyc_attributes.sql
Normal file
92
src/exchangedb/exchange_do_insert_kyc_attributes.sql
Normal file
@ -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';
|
@ -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 $$;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2022 Taler Systems SA
|
||||
Copyright (C) 2022, 2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
@ -29,43 +29,72 @@
|
||||
enum GNUNET_DB_QueryStatus
|
||||
TEH_PG_insert_kyc_attributes (
|
||||
void *cls,
|
||||
uint64_t process_row,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const struct GNUNET_ShortHashCode *kyc_prox,
|
||||
const char *provider_section,
|
||||
const char *birthdate,
|
||||
uint32_t birthday,
|
||||
struct GNUNET_TIME_Timestamp collection_time,
|
||||
struct GNUNET_TIME_Timestamp expiration_time,
|
||||
const char *provider_account_id,
|
||||
const char *provider_legitimization_id,
|
||||
struct GNUNET_TIME_Absolute expiration_time,
|
||||
size_t enc_attributes_size,
|
||||
const void *enc_attributes)
|
||||
const void *enc_attributes,
|
||||
bool require_aml)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_TIME_Timestamp expiration
|
||||
= GNUNET_TIME_absolute_to_timestamp (expiration_time);
|
||||
struct TALER_KycCompletedEventP rep = {
|
||||
.header.size = htons (sizeof (rep)),
|
||||
.header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
|
||||
.h_payto = *h_payto
|
||||
};
|
||||
char *kyc_completed_notify_s
|
||||
= GNUNET_PG_get_event_notify_channel (&rep.header);
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_uint64 (&process_row),
|
||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||
GNUNET_PQ_query_param_auto_from_type (kyc_prox),
|
||||
GNUNET_PQ_query_param_string (provider_section),
|
||||
(NULL == birthdate)
|
||||
GNUNET_PQ_query_param_uint32 (&birthday),
|
||||
(NULL == provider_account_id)
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: GNUNET_PQ_query_param_string (birthdate),
|
||||
: GNUNET_PQ_query_param_string (provider_account_id),
|
||||
(NULL == provider_legitimization_id)
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: GNUNET_PQ_query_param_string (provider_legitimization_id),
|
||||
GNUNET_PQ_query_param_timestamp (&collection_time),
|
||||
GNUNET_PQ_query_param_timestamp (&expiration_time),
|
||||
GNUNET_PQ_query_param_absolute_time (&expiration_time),
|
||||
GNUNET_PQ_query_param_timestamp (&expiration),
|
||||
GNUNET_PQ_query_param_fixed_size (enc_attributes,
|
||||
enc_attributes_size),
|
||||
GNUNET_PQ_query_param_bool (require_aml),
|
||||
GNUNET_PQ_query_param_string (kyc_completed_notify_s),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
bool ok;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_bool ("out_ok",
|
||||
&ok),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
PREPARE (pg,
|
||||
"insert_kyc_attributes",
|
||||
"INSERT INTO kyc_attributes "
|
||||
"(h_payto"
|
||||
",kyc_prox"
|
||||
",provider"
|
||||
",birthdate"
|
||||
",collection_time"
|
||||
",expiration_time"
|
||||
",encrypted_attributes"
|
||||
") VALUES "
|
||||
"($1, $2, $3, $4, $5, $6, $7);");
|
||||
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||
"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);
|
||||
params,
|
||||
rs);
|
||||
GNUNET_free (kyc_completed_notify_s);
|
||||
if (qs < 0)
|
||||
return qs;
|
||||
if (! ok)
|
||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||
return qs;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -366,6 +366,10 @@ struct TALER_EXCHANGE_Keys
|
||||
*/
|
||||
char *asset_type;
|
||||
|
||||
/**
|
||||
* Set to true if tipping is allowed at this exchange.
|
||||
*/
|
||||
bool tipping_allowed;
|
||||
};
|
||||
|
||||
|
||||
|
@ -756,7 +756,6 @@ struct TALER_EXCHANGEDB_TableData
|
||||
struct TALER_PaytoHashP h_payto;
|
||||
struct GNUNET_ShortHashCode kyc_prox;
|
||||
char *provider;
|
||||
char *birthdate; /* NULL allowed! */
|
||||
struct GNUNET_TIME_Timestamp collection_time;
|
||||
struct GNUNET_TIME_Timestamp expiration_time;
|
||||
void *encrypted_attributes;
|
||||
@ -2429,8 +2428,6 @@ typedef void
|
||||
* @param cls closure
|
||||
* @param h_payto account for which the attribute data is stored
|
||||
* @param provider_section provider that must be checked
|
||||
* @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL;
|
||||
* digits can be 0 if exact day, month or year are unknown
|
||||
* @param collection_time when was the data collected
|
||||
* @param expiration_time when does the data expire
|
||||
* @param enc_attributes_size number of bytes in @a enc_attributes
|
||||
@ -2441,7 +2438,6 @@ typedef void
|
||||
void *cls,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const char *provider_section,
|
||||
const char *birthdate,
|
||||
struct GNUNET_TIME_Timestamp collection_time,
|
||||
struct GNUNET_TIME_Timestamp expiration_time,
|
||||
size_t enc_attributes_size,
|
||||
@ -6765,59 +6761,39 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
|
||||
|
||||
/**
|
||||
* Store KYC attribute data.
|
||||
* Store KYC attribute data, update KYC process status and
|
||||
* AML status for the given account.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param process_row KYC process row to update
|
||||
* @param h_payto account for which the attribute data is stored
|
||||
* @param kyc_prox key for similarity search
|
||||
* @param provider_section provider that must be checked
|
||||
* @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL;
|
||||
* digits can be 0 if exact day, month or year are unknown
|
||||
* @param provider_account_id provider account ID
|
||||
* @param provider_legitimization_id provider legitimization ID
|
||||
* @param birthday birthdate of user, in days after 1990, or 0 if unknown or definitively adult
|
||||
* @param collection_time when was the data collected
|
||||
* @param expiration_time when does the data expire
|
||||
* @param enc_attributes_size number of bytes in @a enc_attributes
|
||||
* @param enc_attributes encrypted attribute data
|
||||
* @param require_aml true to trigger AML
|
||||
* @return database transaction status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*insert_kyc_attributes)(
|
||||
void *cls,
|
||||
uint64_t process_row,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const struct GNUNET_ShortHashCode *kyc_prox,
|
||||
const char *provider_section,
|
||||
const char *birthdate,
|
||||
uint32_t birthday,
|
||||
struct GNUNET_TIME_Timestamp collection_time,
|
||||
struct GNUNET_TIME_Timestamp expiration_time,
|
||||
const char *provider_account_id,
|
||||
const char *provider_legitimization_id,
|
||||
struct GNUNET_TIME_Absolute expiration_time,
|
||||
size_t enc_attributes_size,
|
||||
const void *enc_attributes);
|
||||
|
||||
|
||||
/**
|
||||
* Update KYC attribute data.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param h_payto account for which the attribute data is stored
|
||||
* @param kyc_prox key for similarity search
|
||||
* @param provider_section provider that must be checked
|
||||
* @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL;
|
||||
* digits can be 0 if exact day, month or year are unknown
|
||||
* @param collection_time when was the data collected
|
||||
* @param expiration_time when does the data expire
|
||||
* @param enc_attributes_size number of bytes in @a enc_attributes
|
||||
* @param enc_attributes encrypted attribute data
|
||||
* @return database transaction status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*update_kyc_attributes)(
|
||||
void *cls,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const struct GNUNET_ShortHashCode *kyc_prox,
|
||||
const char *provider_section,
|
||||
const char *birthdate,
|
||||
struct GNUNET_TIME_Timestamp collection_time,
|
||||
struct GNUNET_TIME_Timestamp expiration_time,
|
||||
size_t enc_attributes_size,
|
||||
const void *enc_attributes);
|
||||
const void *enc_attributes,
|
||||
bool require_aml);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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 = \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -87,6 +87,12 @@ struct TALER_KYCLOGIC_ProviderDetails
|
||||
*/
|
||||
char *form_id;
|
||||
|
||||
/**
|
||||
* Helper binary to convert attributes returned by
|
||||
* KYCAID into our internal format.
|
||||
*/
|
||||
char *conversion_helper;
|
||||
|
||||
/**
|
||||
* Validity time for a successful KYC process.
|
||||
*/
|
||||
@ -215,6 +221,12 @@ struct TALER_KYCLOGIC_WebhookHandle
|
||||
*/
|
||||
struct PluginState *ps;
|
||||
|
||||
/**
|
||||
* Handle to helper process to extract attributes
|
||||
* we care about.
|
||||
*/
|
||||
struct TALER_JSON_ExternalConversion *econ;
|
||||
|
||||
/**
|
||||
* Our configuration details.
|
||||
*/
|
||||
@ -225,6 +237,11 @@ struct TALER_KYCLOGIC_WebhookHandle
|
||||
*/
|
||||
struct MHD_Connection *connection;
|
||||
|
||||
/**
|
||||
* JSON response we got back, or NULL for none.
|
||||
*/
|
||||
json_t *json_response;
|
||||
|
||||
/**
|
||||
* Verification ID from the service.
|
||||
*/
|
||||
@ -261,6 +278,11 @@ struct TALER_KYCLOGIC_WebhookHandle
|
||||
*/
|
||||
uint64_t process_row;
|
||||
|
||||
/**
|
||||
* HTTP response code we got from KYCAID.
|
||||
*/
|
||||
unsigned int kycaid_response_code;
|
||||
|
||||
/**
|
||||
* HTTP response code to return asynchronously.
|
||||
*/
|
||||
@ -277,6 +299,7 @@ static void
|
||||
kycaid_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
|
||||
{
|
||||
curl_slist_free_all (pd->slist);
|
||||
GNUNET_free (pd->conversion_helper);
|
||||
GNUNET_free (pd->auth_token);
|
||||
GNUNET_free (pd->form_id);
|
||||
GNUNET_free (pd->section);
|
||||
@ -337,6 +360,18 @@ kycaid_load_configuration (void *cls,
|
||||
kycaid_unload_configuration (pd);
|
||||
return NULL;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_filename (ps->cfg,
|
||||
provider_section_name,
|
||||
"KYC_KYCAID_CONVERTER_HELPER",
|
||||
&pd->conversion_helper))
|
||||
{
|
||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||
provider_section_name,
|
||||
"KYC_KYCAID_CONVERTER_HELPER");
|
||||
kycaid_unload_configuration (pd);
|
||||
return NULL;
|
||||
}
|
||||
{
|
||||
char *auth;
|
||||
|
||||
@ -695,11 +730,21 @@ kycaid_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
|
||||
GNUNET_SCHEDULER_cancel (wh->task);
|
||||
wh->task = NULL;
|
||||
}
|
||||
if (NULL != wh->econ)
|
||||
{
|
||||
TALER_JSON_external_conversion_stop (wh->econ);
|
||||
wh->econ = NULL;
|
||||
}
|
||||
if (NULL != wh->job)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (wh->job);
|
||||
wh->job = NULL;
|
||||
}
|
||||
if (NULL != wh->json_response)
|
||||
{
|
||||
json_decref (wh->json_response);
|
||||
wh->json_response = NULL;
|
||||
}
|
||||
GNUNET_free (wh->verification_id);
|
||||
GNUNET_free (wh->applicant_id);
|
||||
GNUNET_free (wh->url);
|
||||
@ -750,6 +795,97 @@ log_failure (json_t *verifications)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Type of a callback that receives a JSON @a result.
|
||||
*
|
||||
* @param cls closure our `struct TALER_KYCLOGIC_WebhookHandle *`
|
||||
* @param status_type how did the process die
|
||||
* @param code termination status code from the process
|
||||
* @param result converted attribute data, NULL on failure
|
||||
*/
|
||||
static void
|
||||
webhook_conversion_cb (void *cls,
|
||||
enum GNUNET_OS_ProcessStatusType status_type,
|
||||
unsigned long code,
|
||||
const json_t *result)
|
||||
{
|
||||
struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
|
||||
struct GNUNET_TIME_Absolute expiration;
|
||||
struct MHD_Response *resp;
|
||||
|
||||
wh->econ = NULL;
|
||||
if ( (0 == code) ||
|
||||
(NULL == result) )
|
||||
{
|
||||
/* No result, but *our helper* was OK => bad input */
|
||||
GNUNET_break_op (0);
|
||||
json_dumpf (wh->json_response,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
resp = TALER_MHD_MAKE_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("kycaid_http_status",
|
||||
wh->kycaid_response_code),
|
||||
GNUNET_JSON_pack_object_incref ("kycaid_body",
|
||||
(json_t *) wh->json_response));
|
||||
wh->cb (wh->cb_cls,
|
||||
wh->process_row,
|
||||
&wh->h_payto,
|
||||
wh->pd->section,
|
||||
wh->applicant_id,
|
||||
wh->verification_id,
|
||||
TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
|
||||
GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
|
||||
NULL,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
resp);
|
||||
kycaid_webhook_cancel (wh);
|
||||
return;
|
||||
}
|
||||
if (NULL == result)
|
||||
{
|
||||
/* Failure in our helper */
|
||||
GNUNET_break (0);
|
||||
json_dumpf (wh->json_response,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
resp = TALER_MHD_MAKE_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("kycaid_http_status",
|
||||
wh->kycaid_response_code),
|
||||
GNUNET_JSON_pack_object_incref ("kycaid_body",
|
||||
(json_t *) wh->json_response));
|
||||
wh->cb (wh->cb_cls,
|
||||
wh->process_row,
|
||||
&wh->h_payto,
|
||||
wh->pd->section,
|
||||
wh->applicant_id,
|
||||
wh->verification_id,
|
||||
TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
|
||||
GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
|
||||
NULL,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
resp);
|
||||
kycaid_webhook_cancel (wh);
|
||||
return;
|
||||
}
|
||||
expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
|
||||
resp = MHD_create_response_from_buffer (0,
|
||||
"",
|
||||
MHD_RESPMEM_PERSISTENT);
|
||||
wh->cb (wh->cb_cls,
|
||||
wh->process_row,
|
||||
&wh->h_payto,
|
||||
wh->pd->section,
|
||||
wh->applicant_id,
|
||||
wh->verification_id,
|
||||
TALER_KYCLOGIC_STATUS_SUCCESS,
|
||||
expiration,
|
||||
result,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
resp);
|
||||
kycaid_webhook_cancel (wh);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called when we're done processing the
|
||||
* HTTP "/applicants/{verification_id}" request.
|
||||
@ -768,246 +904,20 @@ handle_webhook_finished (void *cls,
|
||||
struct MHD_Response *resp;
|
||||
|
||||
wh->job = NULL;
|
||||
wh->kycaid_response_code = response_code;
|
||||
wh->json_response = json_incref ((json_t *) j);
|
||||
switch (response_code)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
{
|
||||
const char *type;
|
||||
const char *profile_status;
|
||||
const char *first_name = NULL;
|
||||
const char *last_name = NULL;
|
||||
const char *middle_name = NULL;
|
||||
const char *dob = NULL;
|
||||
const char *residence_country = NULL;
|
||||
const char *gender = NULL;
|
||||
bool pep = false;
|
||||
bool no_pep = false;
|
||||
const char *company_name = NULL;
|
||||
const char *business_activity_id = NULL;
|
||||
const char *registration_country = NULL;
|
||||
const char *email = NULL;
|
||||
const char *phone = NULL;
|
||||
json_t *addresses = NULL;
|
||||
json_t *documents = NULL;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("type",
|
||||
&type),
|
||||
GNUNET_JSON_spec_string ("profile_status",
|
||||
&profile_status), /* valid, invalid, pending */
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("email",
|
||||
&email),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("phone",
|
||||
&phone),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_json ("addresses",
|
||||
&addresses),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_json ("documents",
|
||||
&documents),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
struct GNUNET_JSON_Specification bspec[] = {
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("company_name",
|
||||
&company_name),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("business_activity_id",
|
||||
&business_activity_id),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("registration_country",
|
||||
®istration_country),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
struct GNUNET_JSON_Specification pspec[] = {
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("first_name",
|
||||
&first_name),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("middle_name",
|
||||
&middle_name),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("last_name",
|
||||
&last_name),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("dob",
|
||||
&dob),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("residence_country",
|
||||
&residence_country),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("gender",
|
||||
&gender),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_bool ("pep",
|
||||
&pep),
|
||||
&no_pep),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
struct GNUNET_JSON_Specification *ispec = NULL;
|
||||
struct GNUNET_TIME_Absolute expiration;
|
||||
bool no_parse;
|
||||
enum TALER_KYCLOGIC_KycUserType ut;
|
||||
|
||||
no_parse = (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (j,
|
||||
spec,
|
||||
NULL, NULL));
|
||||
if (! no_parse)
|
||||
{
|
||||
ut = (0 == strcasecmp ("person",
|
||||
type))
|
||||
? TALER_KYCLOGIC_KYC_UT_INDIVIDUAL
|
||||
: TALER_KYCLOGIC_KYC_UT_BUSINESS;
|
||||
ispec = (ut == TALER_KYCLOGIC_KYC_UT_INDIVIDUAL)
|
||||
? pspec
|
||||
: bspec;
|
||||
no_parse = (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (j,
|
||||
ispec,
|
||||
NULL, NULL));
|
||||
}
|
||||
if (no_parse)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
resp = TALER_MHD_MAKE_JSON_PACK (
|
||||
GNUNET_JSON_pack_uint64 ("kycaid_http_status",
|
||||
response_code),
|
||||
GNUNET_JSON_pack_object_incref ("kycaid_body",
|
||||
(json_t *) j));
|
||||
wh->cb (wh->cb_cls,
|
||||
wh->process_row,
|
||||
&wh->h_payto,
|
||||
wh->pd->section,
|
||||
wh->applicant_id,
|
||||
wh->verification_id,
|
||||
TALER_KYCLOGIC_STATUS_PROVIDER_FAILED,
|
||||
GNUNET_TIME_UNIT_ZERO_ABS, /* expiration */
|
||||
NULL,
|
||||
MHD_HTTP_BAD_GATEWAY,
|
||||
resp);
|
||||
break;
|
||||
}
|
||||
if (0 == strcasecmp ("valid",
|
||||
profile_status = json_string_value (
|
||||
json_object_get (
|
||||
j,
|
||||
"profile_status"));
|
||||
if (0 != strcasecmp ("valid",
|
||||
profile_status))
|
||||
{
|
||||
log_failure (json_object_get (j,
|
||||
"decline_reasons"));
|
||||
}
|
||||
resp = MHD_create_response_from_buffer (0,
|
||||
"",
|
||||
MHD_RESPMEM_PERSISTENT);
|
||||
if (0 == strcasecmp ("valid",
|
||||
profile_status))
|
||||
{
|
||||
json_t *attr;
|
||||
|
||||
if (ut == TALER_KYCLOGIC_KYC_UT_INDIVIDUAL)
|
||||
{
|
||||
char *name = NULL;
|
||||
|
||||
if ( (NULL != last_name) ||
|
||||
(NULL != first_name) ||
|
||||
(NULL != middle_name) )
|
||||
{
|
||||
GNUNET_asprintf (&name,
|
||||
"%s, %s %s",
|
||||
(NULL != last_name)
|
||||
? last_name
|
||||
: "",
|
||||
(NULL != first_name)
|
||||
? first_name
|
||||
: "",
|
||||
(NULL != middle_name)
|
||||
? middle_name
|
||||
: "");
|
||||
}
|
||||
attr = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_BIRTHDATE,
|
||||
dob)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
no_pep
|
||||
? GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_PEP,
|
||||
NULL)
|
||||
: GNUNET_JSON_pack_bool (
|
||||
TALER_ATTRIBUTE_PEP,
|
||||
pep)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_FULL_NAME,
|
||||
name)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_PHONE,
|
||||
phone)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_EMAIL,
|
||||
email)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_RESIDENCES,
|
||||
residence_country))
|
||||
);
|
||||
GNUNET_free (name);
|
||||
}
|
||||
else
|
||||
{
|
||||
attr = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_COMPANY_NAME,
|
||||
company_name)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_PHONE,
|
||||
phone)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_EMAIL,
|
||||
email)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_REGISTRATION_COUNTRY,
|
||||
residence_country))
|
||||
);
|
||||
}
|
||||
// FIXME: do something about addresses & documents!
|
||||
expiration = GNUNET_TIME_relative_to_absolute (wh->pd->validity);
|
||||
wh->cb (wh->cb_cls,
|
||||
wh->process_row,
|
||||
&wh->h_payto,
|
||||
wh->pd->section,
|
||||
wh->applicant_id,
|
||||
wh->verification_id,
|
||||
TALER_KYCLOGIC_STATUS_SUCCESS,
|
||||
expiration,
|
||||
attr,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
resp);
|
||||
json_decref (attr);
|
||||
}
|
||||
else
|
||||
{
|
||||
enum TALER_KYCLOGIC_KycStatus ks;
|
||||
|
||||
@ -1015,6 +925,9 @@ handle_webhook_finished (void *cls,
|
||||
profile_status))
|
||||
? TALER_KYCLOGIC_STATUS_PENDING
|
||||
: TALER_KYCLOGIC_STATUS_USER_ABORTED;
|
||||
resp = MHD_create_response_from_buffer (0,
|
||||
"",
|
||||
MHD_RESPMEM_PERSISTENT);
|
||||
wh->cb (wh->cb_cls,
|
||||
wh->process_row,
|
||||
&wh->h_payto,
|
||||
@ -1026,9 +939,19 @@ handle_webhook_finished (void *cls,
|
||||
NULL,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
resp);
|
||||
break;
|
||||
}
|
||||
GNUNET_JSON_parse_free (ispec);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
wh->econ
|
||||
= TALER_JSON_external_conversion_start (
|
||||
j,
|
||||
&webhook_conversion_cb,
|
||||
wh,
|
||||
wh->pd->conversion_helper,
|
||||
wh->pd->conversion_helper,
|
||||
"-a",
|
||||
wh->pd->auth_token,
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of GNU Taler
|
||||
Copyright (C) 2022 Taler Systems SA
|
||||
Copyright (C) 2022-2023 Taler Systems SA
|
||||
|
||||
Taler is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -75,15 +75,21 @@ struct TALER_KYCLOGIC_ProviderDetails
|
||||
char *section;
|
||||
|
||||
/**
|
||||
* URL of the OAuth2.0 endpoint for KYC checks.
|
||||
* (token/auth)
|
||||
* URL of the Challenger ``/setup`` endpoint for
|
||||
* approving address validations. NULL if not used.
|
||||
*/
|
||||
char *auth_url;
|
||||
char *setup_url;
|
||||
|
||||
/**
|
||||
* URL of the OAuth2.0 endpoint for KYC checks.
|
||||
*/
|
||||
char *login_url;
|
||||
char *authorize_url;
|
||||
|
||||
/**
|
||||
* URL of the OAuth2.0 endpoint for KYC checks.
|
||||
* (token/auth)
|
||||
*/
|
||||
char *token_url;
|
||||
|
||||
/**
|
||||
* URL of the user info access endpoint.
|
||||
@ -147,6 +153,11 @@ struct TALER_KYCLOGIC_InitiateHandle
|
||||
*/
|
||||
struct GNUNET_SCHEDULER_Task *task;
|
||||
|
||||
/**
|
||||
* Handle for the OAuth 2.0 setup request.
|
||||
*/
|
||||
struct GNUNET_CURL_Job *job;
|
||||
|
||||
/**
|
||||
* Continuation to call.
|
||||
*/
|
||||
@ -283,8 +294,9 @@ static void
|
||||
oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
|
||||
{
|
||||
GNUNET_free (pd->section);
|
||||
GNUNET_free (pd->auth_url);
|
||||
GNUNET_free (pd->login_url);
|
||||
GNUNET_free (pd->token_url);
|
||||
GNUNET_free (pd->setup_url);
|
||||
GNUNET_free (pd->authorize_url);
|
||||
GNUNET_free (pd->info_url);
|
||||
GNUNET_free (pd->client_id);
|
||||
GNUNET_free (pd->client_secret);
|
||||
@ -327,12 +339,12 @@ oauth2_load_configuration (void *cls,
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
||||
provider_section_name,
|
||||
"KYC_OAUTH2_AUTH_URL",
|
||||
"KYC_OAUTH2_TOKEN_URL",
|
||||
&s))
|
||||
{
|
||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||
provider_section_name,
|
||||
"KYC_OAUTH2_AUTH_URL");
|
||||
"KYC_OAUTH2_TOKEN_URL");
|
||||
oauth2_unload_configuration (pd);
|
||||
return NULL;
|
||||
}
|
||||
@ -346,23 +358,23 @@ oauth2_load_configuration (void *cls,
|
||||
{
|
||||
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
||||
provider_section_name,
|
||||
"KYC_OAUTH2_AUTH_URL",
|
||||
"KYC_OAUTH2_TOKEN_URL",
|
||||
"not a valid URL");
|
||||
GNUNET_free (s);
|
||||
oauth2_unload_configuration (pd);
|
||||
return NULL;
|
||||
}
|
||||
pd->auth_url = s;
|
||||
pd->token_url = s;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
||||
provider_section_name,
|
||||
"KYC_OAUTH2_LOGIN_URL",
|
||||
"KYC_OAUTH2_AUTHORIZE_URL",
|
||||
&s))
|
||||
{
|
||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||
provider_section_name,
|
||||
"KYC_OAUTH2_LOGIN_URL");
|
||||
"KYC_OAUTH2_AUTHORIZE_URL");
|
||||
oauth2_unload_configuration (pd);
|
||||
return NULL;
|
||||
}
|
||||
@ -376,13 +388,41 @@ oauth2_load_configuration (void *cls,
|
||||
{
|
||||
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
||||
provider_section_name,
|
||||
"KYC_OAUTH2_LOGIN_URL",
|
||||
"KYC_OAUTH2_AUTHORIZE_URL",
|
||||
"not a valid URL");
|
||||
oauth2_unload_configuration (pd);
|
||||
GNUNET_free (s);
|
||||
return NULL;
|
||||
}
|
||||
pd->login_url = s;
|
||||
if (NULL != strchr (s, '#'))
|
||||
{
|
||||
const char *extra = strchr (s, '#');
|
||||
const char *slash = strrchr (s, '/');
|
||||
|
||||
if ( (0 != strcmp (extra,
|
||||
"#setup")) ||
|
||||
(NULL == slash) )
|
||||
{
|
||||
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
||||
provider_section_name,
|
||||
"KYC_OAUTH2_AUTHORIZE_URL",
|
||||
"not a valid authorze URL (bad fragment)");
|
||||
oauth2_unload_configuration (pd);
|
||||
GNUNET_free (s);
|
||||
return NULL;
|
||||
}
|
||||
pd->authorize_url = GNUNET_strndup (s,
|
||||
extra - s);
|
||||
GNUNET_asprintf (&pd->setup_url,
|
||||
"%.*s/setup",
|
||||
(int) (slash - s),
|
||||
s);
|
||||
GNUNET_free (s);
|
||||
}
|
||||
else
|
||||
{
|
||||
pd->authorize_url = s;
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
||||
@ -480,19 +520,20 @@ oauth2_load_configuration (void *cls,
|
||||
* how to begin the OAuth2.0 checking process to
|
||||
* the client.
|
||||
*
|
||||
* @param cls a `struct TALER_KYCLOGIC_InitiateHandle *`
|
||||
* @param ih process to redirect for
|
||||
* @param authorize_url authorization URL to use
|
||||
*/
|
||||
static void
|
||||
initiate_task (void *cls)
|
||||
initiate_with_url (struct TALER_KYCLOGIC_InitiateHandle *ih,
|
||||
const char *authorize_url)
|
||||
{
|
||||
struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
|
||||
|
||||
const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
|
||||
struct PluginState *ps = pd->ps;
|
||||
char *hps;
|
||||
char *url;
|
||||
char legi_s[42];
|
||||
|
||||
ih->task = NULL;
|
||||
GNUNET_snprintf (legi_s,
|
||||
sizeof (legi_s),
|
||||
"%llu",
|
||||
@ -515,16 +556,11 @@ initiate_task (void *cls)
|
||||
}
|
||||
GNUNET_asprintf (&url,
|
||||
"%s?response_type=code&client_id=%s&redirect_uri=%s",
|
||||
pd->login_url,
|
||||
authorize_url,
|
||||
pd->client_id,
|
||||
redirect_uri_encoded);
|
||||
GNUNET_free (redirect_uri_encoded);
|
||||
}
|
||||
/* FIXME-API: why do we *redirect* the client here,
|
||||
instead of making the HTTP request *ourselves*
|
||||
and forwarding the response? This prevents us
|
||||
from using authentication on initiation,
|
||||
(which is desirable for challenger!) */
|
||||
ih->cb (ih->cb_cls,
|
||||
TALER_EC_NONE,
|
||||
url,
|
||||
@ -537,6 +573,142 @@ initiate_task (void *cls)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After we are done with the CURL interaction we
|
||||
* need to update our database state with the information
|
||||
* retrieved.
|
||||
*
|
||||
* @param cls a `struct TALER_KYCLOGIC_InitiateHandle *`
|
||||
* @param response_code HTTP response code from server, 0 on hard error
|
||||
* @param response in JSON, NULL if response was not in JSON format
|
||||
*/
|
||||
static void
|
||||
handle_curl_setup_finished (void *cls,
|
||||
long response_code,
|
||||
const void *response)
|
||||
{
|
||||
struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
|
||||
const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
|
||||
const json_t *j = response;
|
||||
|
||||
ih->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
{
|
||||
const char *nonce;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("nonce",
|
||||
&nonce),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
enum GNUNET_GenericReturnValue res;
|
||||
const char *emsg;
|
||||
unsigned int line;
|
||||
char *url;
|
||||
|
||||
res = GNUNET_JSON_parse (j,
|
||||
spec,
|
||||
&emsg,
|
||||
&line);
|
||||
if (GNUNET_OK != res)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
ih->cb (ih->cb_cls,
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
"Unexpected response from KYC gateway: setup must return a nonce");
|
||||
GNUNET_free (ih);
|
||||
return;
|
||||
}
|
||||
GNUNET_asprintf (&url,
|
||||
"%s/%s",
|
||||
pd->setup_url,
|
||||
nonce);
|
||||
initiate_with_url (ih,
|
||||
url);
|
||||
GNUNET_free (url);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"/setup URL returned HTTP status %u\n",
|
||||
(unsigned int) response_code);
|
||||
ih->cb (ih->cb_cls,
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
"/setup request to OAuth 2.0 backend returned unexpected HTTP status code");
|
||||
GNUNET_free (ih);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logic to asynchronously return the response for how to begin the OAuth2.0
|
||||
* checking process to the client. May first request a dynamic URL via
|
||||
* ``/setup`` if configured to use a client-authenticated setup process.
|
||||
*
|
||||
* @param cls a `struct TALER_KYCLOGIC_InitiateHandle *`
|
||||
*/
|
||||
static void
|
||||
initiate_task (void *cls)
|
||||
{
|
||||
struct TALER_KYCLOGIC_InitiateHandle *ih = cls;
|
||||
const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
|
||||
struct PluginState *ps = pd->ps;
|
||||
char *hdr;
|
||||
struct curl_slist *slist;
|
||||
CURL *eh;
|
||||
|
||||
ih->task = NULL;
|
||||
if (NULL == pd->setup_url)
|
||||
{
|
||||
initiate_with_url (ih,
|
||||
pd->authorize_url);
|
||||
return;
|
||||
}
|
||||
eh = curl_easy_init ();
|
||||
if (NULL == eh)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
ih->cb (ih->cb_cls,
|
||||
TALER_EC_GENERIC_ALLOCATION_FAILURE,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
"curl_easy_init() failed");
|
||||
GNUNET_free (ih);
|
||||
return;
|
||||
}
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_URL,
|
||||
pd->setup_url));
|
||||
GNUNET_asprintf (&hdr,
|
||||
"%s: Bearer %s",
|
||||
MHD_HTTP_HEADER_AUTHORIZATION,
|
||||
pd->client_secret);
|
||||
slist = curl_slist_append (NULL,
|
||||
hdr);
|
||||
ih->job = GNUNET_CURL_job_add2 (ps->curl_ctx,
|
||||
eh,
|
||||
slist,
|
||||
&handle_curl_setup_finished,
|
||||
ih);
|
||||
curl_slist_free_all (slist);
|
||||
GNUNET_free (hdr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initiate KYC check.
|
||||
*
|
||||
@ -584,6 +756,11 @@ oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
|
||||
GNUNET_SCHEDULER_cancel (ih->task);
|
||||
ih->task = NULL;
|
||||
}
|
||||
if (NULL != ih->job)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (ih->job);
|
||||
ih->job = NULL;
|
||||
}
|
||||
GNUNET_free (ih);
|
||||
}
|
||||
|
||||
@ -1002,7 +1179,7 @@ handle_curl_login_finished (void *cls,
|
||||
eh = curl_easy_init ();
|
||||
if (NULL == eh)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
GNUNET_break (0);
|
||||
ph->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_GENERIC_ALLOCATION_FAILURE,
|
||||
@ -1129,7 +1306,7 @@ oauth2_proof (void *cls,
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (ph->eh,
|
||||
CURLOPT_URL,
|
||||
pd->auth_url));
|
||||
pd->token_url));
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (ph->eh,
|
||||
CURLOPT_POST,
|
||||
|
86
src/kyclogic/taler-exchange-kyc-kycaid-converter.sh
Normal file
86
src/kyclogic/taler-exchange-kyc-kycaid-converter.sh
Normal file
@ -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
|
@ -733,6 +733,7 @@ decode_keys_json (const json_t *resp_obj,
|
||||
struct TALER_ExchangePublicKeyP pub;
|
||||
const char *currency;
|
||||
const char *asset_type;
|
||||
bool tipping_allowed = true;
|
||||
json_t *wblwk = NULL;
|
||||
struct GNUNET_JSON_Specification mspec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("denominations_sig",
|
||||
@ -749,6 +750,10 @@ decode_keys_json (const json_t *resp_obj,
|
||||
¤cy),
|
||||
GNUNET_JSON_spec_string ("asset_type",
|
||||
&asset_type),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_bool ("tipping_allowed",
|
||||
&tipping_allowed),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_json ("wallet_balance_limit_without_kyc",
|
||||
&wblwk),
|
||||
@ -819,6 +824,7 @@ decode_keys_json (const json_t *resp_obj,
|
||||
NULL, NULL));
|
||||
key_data->currency = GNUNET_strdup (currency);
|
||||
key_data->asset_type = GNUNET_strdup (asset_type);
|
||||
key_data->tipping_allowed = tipping_allowed;
|
||||
|
||||
/* parse the global fees */
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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)))
|
||||
{
|
||||
|
@ -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": "!",
|
||||
|
@ -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%)%
|
||||
=====================================
|
||||
%(%={{ }}=%)%
|
||||
|
@ -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
|
||||
^
|
||||
=
|
||||
:
|
||||
>
|
@ -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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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": "!",
|
||||
|
@ -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
|
||||
|
@ -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?%)%
|
||||
|
@ -1 +0,0 @@
|
||||
special ==SHOULD NOT BE SEEN==
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user