exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit b0b3f998f13755e5f76d0d32e093826ab5f4793f
parent f9ca3b7442ba0f24b63a631bf679cb1e1e643037
Author: Florian Dold <florian@dold.me>
Date:   Mon,  6 Jul 2026 16:07:40 +0200

exchange/auditor: use separately packaged SPA with fallback

Diffstat:
M.gitmodules | 4----
Dcontrib/aml-spa.lock | 2--
Acontrib/aml-spa/README.md | 4++++
Acontrib/aml-spa/index.html | 11+++++++++++
Dcontrib/auditor-spa.lock | 2--
Acontrib/auditor-spa/README.md | 4++++
Acontrib/auditor-spa/index.html | 11+++++++++++
Dcontrib/check-prebuilt | 37-------------------------------------
Dcontrib/kyc-spa.lock | 2--
Acontrib/kyc-spa/README.md | 4++++
Acontrib/kyc-spa/index.html | 11+++++++++++
Mcontrib/meson.build | 36+++---------------------------------
Dcontrib/wallet-core | 1-
Msrc/auditor/auditor.conf | 3+++
Msrc/auditor/taler-auditor-httpd.c | 23+++++++++++++++++++++++
Msrc/auditor/taler-auditor-httpd.h | 7+++++++
Msrc/auditor/taler-auditor-httpd_spa.c | 39+++++++++++++++++++++++++++++++++++++--
Msrc/exchange/exchange.conf | 9+++++++--
Msrc/exchange/taler-exchange-httpd.c | 34++++++++++++++++++++++++++++++++++
Msrc/exchange/taler-exchange-httpd.h | 14++++++++++++++
Msrc/exchange/taler-exchange-httpd_get-SPA.c | 43+++++++++++++++++++++++++++++++++++++++----
21 files changed, 212 insertions(+), 89 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -5,7 +5,3 @@ [submodule "contrib/gana"] path = contrib/gana url = ../gana.git -[submodule "contrib/wallet-core"] - path = contrib/wallet-core - url = ../taler-typescript-core.git - branch = prebuilt diff --git a/contrib/aml-spa.lock b/contrib/aml-spa.lock @@ -1 +0,0 @@ -1.5.14 -\ No newline at end of file diff --git a/contrib/aml-spa/README.md b/contrib/aml-spa/README.md @@ -0,0 +1,4 @@ +This directory contains a fallback for the AML SPA. + +The real KYC SPA is built from +taler-typescript-core.git/packages/taler-exchange-aml-webui. diff --git a/contrib/aml-spa/index.html b/contrib/aml-spa/index.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<body> + <h1>Taler Exchange AML Web UI not installed</h1> + <p>This is a fallback page shown when the Taler Exchange AML Web UI is not + available.</p> + <p>Please make sure the package is installed and the + <code>AML_SPA_DIR</code> option in the <code>[exchange]</code> section of + the taler-exchange configuration is set correctly.</p> +</body> +</html> diff --git a/contrib/auditor-spa.lock b/contrib/auditor-spa.lock @@ -1 +0,0 @@ -1.5.14 -\ No newline at end of file diff --git a/contrib/auditor-spa/README.md b/contrib/auditor-spa/README.md @@ -0,0 +1,4 @@ +This directory contains a fallback for the Auditor SPA. + +The real Auditor SPA is built from +taler-typescript-core.git/packages/taler-auditor-webui. diff --git a/contrib/auditor-spa/index.html b/contrib/auditor-spa/index.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<body> + <h1>Taler Auditor Web UI not installed</h1> + <p>This is a fallback page shown when the Taler Exchange KYC Web UI is not + available.</p> + <p>Please make sure the package is installed and the + <code>SPA_DIR</code> option in the <code>[exchange]</code> section of + the taler-auditor configuration is set correctly.</p> +</body> +</html> diff --git a/contrib/check-prebuilt b/contrib/check-prebuilt @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys - -failed = False -contrib = os.path.abspath(os.path.dirname(__file__)) - -auditor_spa_ver_lock = open(contrib + "/" + "auditor-spa.lock").read().strip() -auditor_spa_ver_prebuilt = open(contrib + "/" + "wallet-core/auditor-backoffice/version.txt").read().strip() - -aml_spa_ver_lock = open(contrib + "/" + "aml-spa.lock").read().strip() -aml_spa_ver_prebuilt = open(contrib + "/" + "wallet-core/aml-backoffice/version.txt").read().strip() - -kyc_spa_ver_lock = open(contrib + "/" + "kyc-spa.lock").read().strip() -kyc_spa_ver_prebuilt = open(contrib + "/" + "wallet-core/kyc/version.txt").read().strip() - -if aml_spa_ver_lock != aml_spa_ver_prebuilt: - print("AML SPA version mismatch (contrib/aml-spa.lock)") - print("lockfile has version", aml_spa_ver_lock) - print("prebuilt has version", aml_spa_ver_prebuilt) - failed = True - -if kyc_spa_ver_lock != kyc_spa_ver_prebuilt: - print("KYC SPA version mismatch (contrib/kyc-spa.lock)") - print("lockfile has version", kyc_spa_ver_lock) - print("prebuilt has version", kyc_spa_ver_prebuilt) - failed = True - -if auditor_spa_ver_lock != auditor_spa_ver_prebuilt: - print("auditor SPA version mismatch (contrib/auditor-spa.lock)") - print("lockfile has version", auditor_spa_ver_lock) - print("prebuilt has version", auditor_spa_ver_prebuilt) - failed = True - -if failed: - sys.exit(1) diff --git a/contrib/kyc-spa.lock b/contrib/kyc-spa.lock @@ -1 +0,0 @@ -1.5.14 -\ No newline at end of file diff --git a/contrib/kyc-spa/README.md b/contrib/kyc-spa/README.md @@ -0,0 +1,4 @@ +This directory contains a fallback for the KYC SPA. + +The real KYC SPA is built from +taler-typescript-core.git/packages/taler-exchange-kyc-webui. diff --git a/contrib/kyc-spa/index.html b/contrib/kyc-spa/index.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> +<body> + <h1>Taler Exchange KYC Web UI not installed</h1> + <p>This is a fallback page shown when the Taler Exchange KYC Web UI is not + available.</p> + <p>Please make sure the package is installed and the + <code>KYC_SPA_DIR</code> option in the <code>[exchange]</code> section of + the taler-exchange configuration is set correctly.</p> +</body> +</html> diff --git a/contrib/meson.build b/contrib/meson.build @@ -73,15 +73,7 @@ endforeach # be used by code generated by 'bootstrap'! dist_amlspapkgdata_DATA = [ - 'wallet-core/aml-backoffice/bof', - 'wallet-core/aml-backoffice/build-metadata.json', - 'wallet-core/aml-backoffice/forms.json', - 'wallet-core/aml-backoffice/index.css', - 'wallet-core/aml-backoffice/index.css.map', - 'wallet-core/aml-backoffice/index.html', - 'wallet-core/aml-backoffice/index.js', - 'wallet-core/aml-backoffice/index.js.map', - 'wallet-core/aml-backoffice/version.txt', + 'aml-spa/index.html', ] install_data( sources: dist_amlspapkgdata_DATA, @@ -89,15 +81,7 @@ install_data( ) dist_kycspapkgdata_DATA = [ - 'wallet-core/kyc/bof', - 'wallet-core/kyc/build-metadata.json', - 'wallet-core/kyc/forms.json', - 'wallet-core/kyc/index.css', - 'wallet-core/kyc/index.css.map', - 'wallet-core/kyc/index.html', - 'wallet-core/kyc/index.js', - 'wallet-core/kyc/index.js.map', - 'wallet-core/kyc/version.txt', + 'kyc-spa/index.html', ] install_data( sources: dist_kycspapkgdata_DATA, @@ -105,21 +89,7 @@ install_data( ) dist_auditorspapkgdata_DATA = [ - 'wallet-core/auditor-backoffice/bof', - 'wallet-core/auditor-backoffice/build-metadata.json', - 'wallet-core/auditor-backoffice/index.css', - 'wallet-core/auditor-backoffice/index.css.map', - 'wallet-core/auditor-backoffice/index.html', - 'wallet-core/auditor-backoffice/index.js', - 'wallet-core/auditor-backoffice/index.js.map', - 'wallet-core/auditor-backoffice/languageicon-LWKRUH5D.svg', - 'wallet-core/auditor-backoffice/logo-2021-VSZSJ4QZ.svg', - 'wallet-core/auditor-backoffice/materialdesignicons-webfont-4.9.95-7W2GKO6O.woff2', - 'wallet-core/auditor-backoffice/materialdesignicons-webfont-4.9.95-GELXKCZG.ttf', - 'wallet-core/auditor-backoffice/materialdesignicons-webfont-4.9.95-HENMUVWG.eot', - 'wallet-core/auditor-backoffice/materialdesignicons-webfont-4.9.95-WP2MXZKI.woff', - 'wallet-core/auditor-backoffice/version.txt', - 'wallet-core/auditor-backoffice/XRXV3I6Li01BKofINeaE-34D53UZZ.ttf', + 'auditor-spa/index.html', ] install_data( sources: dist_auditorspapkgdata_DATA, diff --git a/contrib/wallet-core b/contrib/wallet-core @@ -1 +0,0 @@ -Subproject commit 45c7768f9153846198dac99b6a9015477b9d7d2a diff --git a/src/auditor/auditor.conf b/src/auditor/auditor.conf @@ -31,3 +31,6 @@ UNIXPATH_MODE = 660 # HTTP port the auditor listens to PORT = 8083 + +# Location of the (externally packaged) auditor SPA +SPA_DIR = $PREFIX/share/taler-auditor-webui/ diff --git a/src/auditor/taler-auditor-httpd.c b/src/auditor/taler-auditor-httpd.c @@ -128,6 +128,14 @@ static struct TALER_AuditorPublicKeyP auditor_pub; struct TALER_MasterPublicKeyP TAH_master_public_key; /** + * Exchange master public key (according to the + * configuration). (global) + */ +struct TALER_MasterPublicKeyP TAH_master_public_key; + +char *TAH_spa_dir; + +/** * Default timeout in seconds for HTTP requests. */ static unsigned int connection_timeout = 30; @@ -1296,6 +1304,21 @@ auditor_serve_process_config (void) GNUNET_CRYPTO_eddsa_key_get_public (&eddsa_priv, &auditor_pub.eddsa_pub); } + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, + "auditor", + "SPA_DIR", + &TAH_spa_dir)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Loading auditor SPA from %s\n", + TAH_spa_dir); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Loading exchange AML SPA from default location\n"); + } return GNUNET_OK; } diff --git a/src/auditor/taler-auditor-httpd.h b/src/auditor/taler-auditor-httpd.h @@ -45,6 +45,13 @@ extern struct TALER_EXCHANGEDB_PostgresContext *TAH_epg; extern struct TALER_MasterPublicKeyP TAH_master_public_key; /** + * Absolute directory path where the auditor SPA data is located. + * + * Can be NULL. + */ +extern char *TAH_spa_dir; + +/** * Our currency. */ extern char *TAH_currency; diff --git a/src/auditor/taler-auditor-httpd_spa.c b/src/auditor/taler-auditor-httpd_spa.c @@ -53,11 +53,46 @@ TAH_spa_handler ( } +/** + * Load the SPA data from an directory. + * + * If the directory is not found, + * fall back to a directory owned by the taler-exchange package. + * + * @param spa_abs_dir absolute location of the SPA, + * typically packaged separately from the exchange + * @param spa_fallback_dir fallback directory, relative + * to the project data. + * @returns loaded SPA or NULL on error + */ +static struct TALER_MHD_Spa * +spa_load (const char *spa_abs_dir, + const char *spa_fallback_dir) +{ + bool have_spa = false; + GNUNET_assert (NULL != spa_fallback_dir); + if (NULL != spa_abs_dir) + { + struct stat sb; + have_spa = stat (spa_abs_dir, &sb) == 0 && S_ISDIR (sb.st_mode); + if (have_spa) + { + return TALER_MHD_spa_load_dir (spa_abs_dir); + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "SPA not found at '%s', using fallback\n", + spa_abs_dir); + } + return TALER_MHD_spa_load (TALER_AUDITOR_project_data (), + spa_fallback_dir); +} + + enum GNUNET_GenericReturnValue TAH_spa_init () { - spa = TALER_MHD_spa_load (TALER_AUDITOR_project_data (), - "spa/"); + spa = spa_load (TAH_spa_dir, + "spa/"); if (NULL == spa) { GNUNET_break (0); diff --git a/src/exchange/exchange.conf b/src/exchange/exchange.conf @@ -141,6 +141,12 @@ PRIVACY_ETAG = exchange-pp-v0 # Where are the Typst templates for form rendering. TYPST_TEMPLATES = @taler-exchange +# Location of the (externally packaged) AML SPA +AML_SPA_DIR = $PREFIX/share/taler-exchange-aml-webui/ + +# Location of the (externally packaged) KYC SPA +KYC_SPA_DIR = $PREFIX/share/taler-exchange-kyc-webui/ + [exchange-sanctionscheck] # Name where we store the sanctions check offset. MIN_ROW_FILENAME = ${HOME}/.cache/sanctionscheck-offset.bin @@ -161,4 +167,4 @@ FREEZE_CONFIDENCE_LIMIT = 0.9 # good 0.9 match with a super-high 1.0 confidence would not (0.9 < 0.95). # OTOH, a bad 0.2 match with super-low 0.1 confidence would again # trigger (2.0 > 0.95). -INVESTIGATION_LIMIT = 0.9 -\ No newline at end of file +INVESTIGATION_LIMIT = 0.9 diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -151,6 +151,10 @@ char *TEH_tos_etag; char *TEH_shopping_url; +char *TEH_aml_spa_dir; + +char *TEH_kyc_spa_dir; + /** * Where to redirect users from "/"? */ @@ -2383,6 +2387,36 @@ exchange_serve_process_config (const char *cfg_fn) "invalid URL"); return GNUNET_SYSERR; } + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (TEH_cfg, + "exchange", + "AML_SPA_DIR", + &TEH_aml_spa_dir)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Loading exchange AML SPA from %s\n", + TEH_aml_spa_dir); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Loading exchange AML SPA from default location\n"); + } + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (TEH_cfg, + "exchange", + "KYC_SPA_DIR", + &TEH_kyc_spa_dir)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Loading exchange KYC SPA from %s\n", + TEH_kyc_spa_dir); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Loading exchange KYC SPA from default location\n"); + } { char *master_public_key_str; diff --git a/src/exchange/taler-exchange-httpd.h b/src/exchange/taler-exchange-httpd.h @@ -171,6 +171,20 @@ extern char *TEH_tos_etag; extern char *TEH_shopping_url; /** + * Absolute directory path where the aml SPA data is located. + * + * Can be NULL. + */ +extern char *TEH_aml_spa_dir; + +/** + * Absolute directory path where the aml SPA data is located. + * + * Can be NULL. + */ +extern char *TEH_kyc_spa_dir; + +/** * Linear STEFAN parameter. */ extern float TEH_stefan_lin; diff --git a/src/exchange/taler-exchange-httpd_get-SPA.c b/src/exchange/taler-exchange-httpd_get-SPA.c @@ -82,18 +82,53 @@ TEH_handler_kyc_spa (struct TEH_RequestContext *rc, } +/** + * Load the SPA data from an directory. + * + * If the directory is not found, + * fall back to a directory owned by the taler-exchange package. + * + * @param spa_abs_dir absolute location of the SPA, + * typically packaged separately from the exchange + * @param spa_fallback_dir fallback directory, relative + * to the project data. + * @returns loaded SPA or NULL on error + */ +static struct TALER_MHD_Spa * +spa_load (const char *spa_abs_dir, + const char *spa_fallback_dir) +{ + bool have_spa = false; + GNUNET_assert (NULL != spa_fallback_dir); + if (NULL != spa_abs_dir) + { + struct stat sb; + have_spa = stat (spa_abs_dir, &sb) == 0 && S_ISDIR (sb.st_mode); + if (have_spa) + { + return TALER_MHD_spa_load_dir (spa_abs_dir); + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "SPA not found at '%s', using fallback\n", + spa_abs_dir); + } + return TALER_MHD_spa_load (TALER_EXCHANGE_project_data (), + spa_fallback_dir); +} + + enum GNUNET_GenericReturnValue TEH_spa_init () { - aml_spa = TALER_MHD_spa_load (TALER_EXCHANGE_project_data (), - "aml-spa/"); + aml_spa = spa_load (TEH_aml_spa_dir, + "aml-spa/"); if (NULL == aml_spa) { GNUNET_break (0); return GNUNET_SYSERR; } - kyc_spa = TALER_MHD_spa_load (TALER_EXCHANGE_project_data (), - "kyc-spa/"); + kyc_spa = spa_load (TEH_kyc_spa_dir, + "kyc-spa/"); if (NULL == kyc_spa) { GNUNET_break (0);