libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit bf2d191a7efdac6003a5e848a992ceb08f0e12fe
parent 2b44cefd44359805a9ea78293c6450be18b8ecf9
Author: Antoine A <>
Date:   Mon, 22 Jul 2024 18:55:00 +0200

nexus: lazily load nexus ebics config

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/Config.kt | 52++++++++++++++++++++++++++++++----------------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/api/RevenueApi.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt | 5+++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt | 9+++++----
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt | 3++-
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt | 12++++++++----
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt | 4++--
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt | 8++++----
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/test/TxCheck.kt | 4++--
Mnexus/src/test/kotlin/CliTest.kt | 3++-
Mnexus/src/test/kotlin/EbicsTest.kt | 2+-
Mtestbench/src/main/kotlin/Main.kt | 3++-
Mtestbench/src/test/kotlin/Iso20022Test.kt | 3++-
18 files changed, 69 insertions(+), 51 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Config.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Config.kt @@ -40,21 +40,8 @@ class NexusSubmitConfig(config: TalerConfig) { val frequencyRaw = section.string("frequency").require() } -class ApiConfig(section: TalerConfigSection) { - val authMethod = section.requireAuthMethod() -} - -/** Configuration for libeufin-nexus */ -class NexusConfig internal constructor (private val cfg: TalerConfig) { - private val sect = cfg.section("nexus-ebics") - - val dbCfg: DatabaseConfig by lazy { cfg.dbConfig() } - val serverCfg: ServerConfig by lazy { - cfg.loadServerConfig("nexus-httpd") - } - - /** The bank's currency */ - val currency = sect.string("currency").require() +class NexusEbicsConfig(config: TalerConfig, val currency: String) { + private val sect = config.section("nexus-ebics") /** The bank base URL */ val hostBaseUrl = sect.string("host_base_url").require() /** The bank EBICS host ID */ @@ -71,13 +58,6 @@ class NexusConfig internal constructor (private val cfg: TalerConfig) { ) /** Bank account payto */ val payto = IbanPayto.build(account.iban, account.bic, account.name) - /** Path where we store the bank public keys */ - val bankPublicKeysPath = sect.path("bank_public_keys_file").require() - /** Path where we store our private keys */ - val clientPrivateKeysPath = sect.path("client_private_keys_file").require() - - val fetch = NexusFetchConfig(cfg) - val submit = NexusSubmitConfig(cfg) val dialect = sect.map("bank_dialect", "dialect", mapOf( "postfinance" to Dialect.postfinance, @@ -87,6 +67,34 @@ class NexusConfig internal constructor (private val cfg: TalerConfig) { "normal" to AccountType.normal, "exchange" to AccountType.exchange )).require() + + /** Path where we store the bank public keys */ + val bankPublicKeysPath = sect.path("bank_public_keys_file").require() + /** Path where we store our private keys */ + val clientPrivateKeysPath = sect.path("client_private_keys_file").require() + + val fetch = NexusFetchConfig(config) + val submit = NexusSubmitConfig(config) +} + +class ApiConfig(section: TalerConfigSection) { + val authMethod = section.requireAuthMethod() +} + +/** Configuration for libeufin-nexus */ +class NexusConfig internal constructor (private val cfg: TalerConfig) { + private val sect = cfg.section("nexus-ebics") + + val dbCfg by lazy { cfg.dbConfig() } + val serverCfg by lazy { + cfg.loadServerConfig("nexus-httpd") + } + + /** The bank's currency */ + val currency = sect.string("currency").require() + + val ebics by lazy { NexusEbicsConfig(cfg, currency) } + val wireGatewayApiCfg = cfg.section("nexus-httpd-wire-gateway-api").apiConf() val revenueApiCfg = cfg.section("nexus-httpd-revenue-api").apiConf() } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/KeyFiles.kt @@ -202,7 +202,7 @@ fun loadClientKeys(location: Path): ClientPrivateKeysFile? = loadJsonFile(locati * @param cfg configuration handle. * @return both client and bank keys */ -fun expectFullKeys(cfg: NexusConfig): Pair<ClientPrivateKeysFile, BankPublicKeysFile> { +fun expectFullKeys(cfg: NexusEbicsConfig): Pair<ClientPrivateKeysFile, BankPublicKeysFile> { val clientKeys = loadClientKeys(cfg.clientPrivateKeysPath) if (clientKeys == null) { throw Exception("Missing client private keys file at '${cfg.clientPrivateKeysPath}', run 'libeufin-nexus ebics-setup' first") diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/PDF.kt @@ -36,7 +36,7 @@ import java.time.format.DateTimeFormatter */ fun generateKeysPdf( clientKeys: ClientPrivateKeysFile, - cfg: NexusConfig + cfg: NexusEbicsConfig ): ByteArray { val po = ByteArrayOutputStream() val pdfWriter = PdfWriter(po) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/RevenueApi.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/RevenueApi.kt @@ -41,7 +41,7 @@ fun Routing.revenueApi(db: Database, cfg: NexusConfig) = authApi(cfg.revenueApiC if (items.isEmpty()) { call.respond(HttpStatusCode.NoContent) } else { - call.respond(RevenueIncomingHistory(items, cfg.payto)) + call.respond(RevenueIncomingHistory(items, cfg.ebics.payto)) } } } \ No newline at end of file diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt @@ -77,7 +77,7 @@ fun Routing.wireGatewayApi(db: Database, cfg: NexusConfig) = authApi(cfg.wireGat if (items.isEmpty()) { call.respond(HttpStatusCode.NoContent) } else { - call.respond(reduce(items, cfg.payto)) + call.respond(reduce(items, cfg.ebics.payto)) } } get("/taler-wire-gateway/history/incoming") { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsFetch.kt @@ -115,7 +115,7 @@ suspend fun ingestIncomingPayment( /** Ingest an EBICS [payload] of [document] into [db] */ private suspend fun ingestPayload( db: Database, - cfg: NexusConfig, + cfg: NexusEbicsConfig, payload: InputStream, document: SupportedDocument ) { @@ -290,7 +290,8 @@ class EbicsFetch: CliktCommand("Downloads and parse EBICS files from the bank an private val ebicsLog by ebicsLogOption() override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + nexusConfig(common.config).withDb { db, nexusCgf -> + val cfg = nexusCgf.ebics val (clientKeys, bankKeys) = expectFullKeys(cfg) val client = EbicsClient( cfg, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt @@ -70,7 +70,7 @@ private fun askUserToAcceptKeys(bankKeys: BankPublicKeysFile): Boolean { * keys */ suspend fun doKeysRequestAndUpdateState( - cfg: NexusConfig, + cfg: NexusEbicsConfig, privs: ClientPrivateKeysFile, client: HttpClient, ebicsLogger: EbicsLogger, @@ -129,7 +129,7 @@ suspend fun doKeysRequestAndUpdateState( * @param privs client private keys. * @param cfg configuration handle. */ -private fun makePdf(privs: ClientPrivateKeysFile, cfg: NexusConfig) { +private fun makePdf(privs: ClientPrivateKeysFile, cfg: NexusEbicsConfig) { val pdf = generateKeysPdf(privs, cfg) val path = Path("/tmp/libeufin-nexus-keys-${Instant.now().epochSecond}.pdf") try { @@ -160,7 +160,8 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") { * This function collects the main steps of setting up an EBICS access. */ override fun run() = cliCmd(logger, common.log) { - val cfg = nexusConfig(common.config) + val nexusCfg = nexusConfig(common.config) + val cfg = nexusCfg.ebics val client = httpClient() val ebicsLogger = EbicsLogger(ebicsLog) @@ -209,7 +210,7 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") { // Check account information logger.info("Doing administrative request HKD") try { - cfg.withDb { db, cfg -> + nexusCfg.withDb { db, _ -> EbicsClient( cfg, client, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt @@ -94,7 +94,8 @@ class EbicsSubmit : CliktCommand("Submits pending initiated payments found in th private val ebicsLog by ebicsLogOption() override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + nexusConfig(common.config).withDb { db, nexusCfg -> + val cfg = nexusCfg.ebics val (clientKeys, bankKeys) = expectFullKeys(cfg) val client = EbicsClient( cfg, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt @@ -44,7 +44,8 @@ class Wss: CliktCommand("Listen to EBICS instant notification over websocket") { override fun run() = cliCmd(logger, common.log) { val backoff = ExpoBackoffDecorr() - nexusConfig(common.config).withDb { db, cfg -> + nexusConfig(common.config).withDb { db, nexusCgf -> + val cfg = nexusCgf.ebics val (clientKeys, bankKeys) = expectFullKeys(cfg) val httpClient = httpClient() val client = EbicsClient( @@ -87,7 +88,8 @@ class FakeIncoming: CliktCommand("Genere a fake incoming payment") { ).convert { Payto.parse(it).expectIban() } override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + nexusConfig(common.config).withDb { db, nexusCgf -> + val cfg = nexusCgf.ebics val subject = requireNotNull(payto.message ?: subject) { "Missing subject" } val amount = requireNotNull(payto.amount ?: amount) { "Missing amount" } @@ -118,7 +120,8 @@ class TxCheck: CliktCommand("Check transaction semantic") { private val common by CommonOption() override fun run() = cliCmd(logger, common.log) { - val cfg = nexusConfig(common.config) + val nexusCgf = nexusConfig(common.config) + val cfg = nexusCgf.ebics val (clientKeys, bankKeys) = expectFullKeys(cfg) val doc = EbicsDocument.acknowledgement.doc() val order = cfg.dialect.downloadDoc(doc, false) @@ -160,7 +163,8 @@ class EbicsDownload: CliktCommand("Perform EBICS requests", name = "ebics-btd") class DryRun: Exception() override fun run() = cliCmd(logger, common.log) { - nexusConfig(common.config).withDb { db, cfg -> + nexusConfig(common.config).withDb { db, nexusCgf -> + val cfg = nexusCgf.ebics val (clientKeys, bankKeys) = expectFullKeys(cfg) val pinnedStartVal = pinnedStart val pinnedStartArg = if (pinnedStartVal != null) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt @@ -20,7 +20,7 @@ package tech.libeufin.nexus.ebics import org.w3c.dom.Document -import tech.libeufin.nexus.NexusConfig +import tech.libeufin.nexus.NexusEbicsConfig import tech.libeufin.nexus.XmlBuilder import tech.libeufin.nexus.XmlDestructor import java.io.InputStream @@ -45,7 +45,7 @@ data class OrderInfo ( ) object EbicsAdministrative { - fun HEV(cfg: NexusConfig): ByteArray { + fun HEV(cfg: NexusEbicsConfig): ByteArray { return XmlBuilder.toBytes("ebicsHEVRequest") { attr("xmlns", "http://www.ebics.org/H000") el("HostID", cfg.ebicsHostId) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsBTS.kt @@ -37,7 +37,7 @@ fun Instant.xmlDateTime(): String = /** EBICS protocol for business transactions */ class EbicsBTS( - val cfg: NexusConfig, + val cfg: NexusEbicsConfig, val bankKeys: BankPublicKeysFile, val clientKeys: ClientPrivateKeysFile, val order: EbicsOrder diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt @@ -114,7 +114,7 @@ suspend fun EbicsBTS.postBTS( /** High level EBICS client */ class EbicsClient( - val cfg: NexusConfig, + val cfg: NexusEbicsConfig, val client: HttpClient, val db: Database, val ebicsLogger: EbicsLogger, @@ -261,7 +261,7 @@ class EbicsClient( suspend fun HEV( client: HttpClient, - cfg: NexusConfig, + cfg: NexusEbicsConfig, ebicsLogger: EbicsLogger ): List<VersionNumber> { logger.info("Doing administrative request HEV") @@ -272,7 +272,7 @@ suspend fun HEV( } suspend fun keyManagement( - cfg: NexusConfig, + cfg: NexusEbicsConfig, privs: ClientPrivateKeysFile, client: HttpClient, ebicsLogger: EbicsLogger, @@ -294,7 +294,7 @@ class PreparedUploadData( /** Signs, encrypts and format EBICS BTS payload */ fun prepareUploadPayload( - cfg: NexusConfig, + cfg: NexusEbicsConfig, clientKeys: ClientPrivateKeysFile, bankKeys: BankPublicKeysFile, payload: ByteArray, diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsKeyMng.kt @@ -34,7 +34,7 @@ import java.time.Instant /** EBICS protocol for key management */ class EbicsKeyMng( - private val cfg: NexusConfig, + private val cfg: NexusEbicsConfig, private val clientKeys: ClientPrivateKeysFile, private val ebics3: Boolean ) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/test/TxCheck.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/test/TxCheck.kt @@ -24,7 +24,7 @@ import tech.libeufin.common.fmt import tech.libeufin.common.rand import tech.libeufin.nexus.BankPublicKeysFile import tech.libeufin.nexus.ClientPrivateKeysFile -import tech.libeufin.nexus.NexusConfig +import tech.libeufin.nexus.NexusEbicsConfig import tech.libeufin.nexus.ebics.EbicsBTS import tech.libeufin.nexus.ebics.EbicsOrder import tech.libeufin.nexus.ebics.postBTS @@ -47,7 +47,7 @@ data class TxCheckResult( */ suspend fun txCheck( client: HttpClient, - cfg: NexusConfig, + cfg: NexusEbicsConfig, clientKeys: ClientPrivateKeysFile, bankKeys: BankPublicKeysFile, fetchOrder: EbicsOrder, diff --git a/nexus/src/test/kotlin/CliTest.kt b/nexus/src/test/kotlin/CliTest.kt @@ -51,7 +51,8 @@ class CliTest { val cmds = listOf("ebics-submit", "ebics-fetch") val allCmds = listOf("ebics-submit", "ebics-fetch", "ebics-setup") val conf = "conf/test.conf" - val cfg = nexusConfig(Path(conf)) + val nexusCfg = nexusConfig(Path(conf)) + val cfg = nexusCfg.ebics val clientKeysPath = cfg.clientPrivateKeysPath val bankKeysPath = cfg.bankPublicKeysPath clientKeysPath.parent!!.createDirectories() diff --git a/nexus/src/test/kotlin/EbicsTest.kt b/nexus/src/test/kotlin/EbicsTest.kt @@ -66,7 +66,7 @@ class EbicsTest { // Mainly tests that the function does not throw any error. @Test fun keysPdf() = conf { config -> - val pdf = generateKeysPdf(clientKeys, config) + val pdf = generateKeysPdf(clientKeys, config.ebics) Path("/tmp/libeufin-nexus-test-keys.pdf").writeBytes(pdf) } } \ No newline at end of file diff --git a/testbench/src/main/kotlin/Main.kt b/testbench/src/main/kotlin/Main.kt @@ -98,7 +98,8 @@ class Cli : CliktCommand("Run integration tests on banks provider") { [libeufin-nexusdb-postgres] CONFIG = postgres:///libeufintestbench """) - val cfg = nexusConfig(conf) + val nexusCfg = nexusConfig(conf) + val cfg = nexusCfg.ebics // Check if platform is known val kind = when (cfg.hostBaseUrl) { diff --git a/testbench/src/test/kotlin/Iso20022Test.kt b/testbench/src/test/kotlin/Iso20022Test.kt @@ -56,7 +56,8 @@ class Iso20022Test { if (!file.isDirectory()) continue val fetch = file.resolve("fetch") if (!fetch.exists()) continue - val cfg = nexusConfig(platform.resolve("ebics.conf")) + val nexusCfg = nexusConfig(platform.resolve("ebics.conf")) + val cfg = nexusCfg.ebics val currency = cfg.currency val dialect = cfg.dialect for (log in fetch.listDirectoryEntries()) {