libeufin

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

commit 87c42907a6b4d556f290985263f4a2ecd5b524c4
parent c8e8976cb2b985bf6c396c607560cdd7e1a6b2b9
Author: Florian Dold <florian@dold.me>
Date:   Mon, 27 Nov 2023 19:46:44 +0100

shared config subcommand for nexus and bank

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/Authentication.kt | 4++++
Mbank/src/main/kotlin/tech/libeufin/bank/Config.kt | 2+-
Mbank/src/main/kotlin/tech/libeufin/bank/Main.kt | 74+-------------------------------------------------------------------------
Mbank/src/main/kotlin/tech/libeufin/bank/db/NotificationWatcher.kt | 4++++
Mbank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt | 4++++
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt | 4++++
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt | 6++++--
Mutil/build.gradle | 3++-
Mutil/src/main/kotlin/Config.kt | 3---
Autil/src/main/kotlin/ConfigCli.kt | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mutil/src/main/kotlin/DB.kt | 1+
Mutil/src/main/kotlin/HTTP.kt | 4++++
Mutil/src/main/kotlin/IbanPayto.kt | 4++++
Mutil/src/main/kotlin/TalerConfig.kt | 5++++-
Mutil/src/main/kotlin/UnixDomainSocket.kt | 8++++----
Mutil/src/main/kotlin/XMLUtil.kt | 4++++
Mutil/src/main/kotlin/startServer.kt | 4++++
Mutil/src/main/kotlin/time.kt | 4++++
19 files changed, 173 insertions(+), 86 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Authentication.kt b/bank/src/main/kotlin/tech/libeufin/bank/Authentication.kt @@ -27,9 +27,13 @@ import io.ktor.util.pipeline.PipelineContext import java.time.Instant import net.taler.common.errorcodes.TalerErrorCode import net.taler.wallet.crypto.Base32Crockford +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.bank.AccountDAO.* import tech.libeufin.util.* +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.Authentication") + private val AUTH_IS_ADMIN = AttributeKey<Boolean>("is_admin"); /** Restrict route access to admin */ diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt @@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory import tech.libeufin.util.DatabaseConfig private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.bank.Config") -private val BANK_CONFIG_SOURCE = ConfigSource("libeufin", "libeufin-bank", "libeufin-bank") +val BANK_CONFIG_SOURCE = ConfigSource("libeufin", "libeufin-bank", "libeufin-bank") /** * Application the parsed configuration. diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt @@ -30,7 +30,6 @@ import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.http.content.* import io.ktor.serialization.kotlinx.json.* -import io.ktor.server.application.* import io.ktor.server.engine.* import io.ktor.server.netty.* import io.ktor.server.plugins.* @@ -42,7 +41,6 @@ import io.ktor.server.request.* import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.utils.io.* -import io.ktor.utils.io.jvm.javaio.* import java.time.Duration import java.util.zip.DataFormatException import java.util.zip.Inflater @@ -51,7 +49,6 @@ import java.io.File import kotlin.system.exitProcess import kotlinx.coroutines.* import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.descriptors.* import kotlinx.serialization.json.* import net.taler.common.errorcodes.TalerErrorCode import org.slf4j.Logger @@ -395,79 +392,10 @@ class ChangePw : CliktCommand("Change account password", name = "passwd") { } } -// TODO remove ? -class BankConfigDump : CliktCommand("Dump the configuration", name = "dump") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) - - override fun run() { - val config = talerConfig(configFile) - println("# install path: ${config.getInstallPath()}") - config.load(this.configFile) - println(config.stringify()) - } -} - -class BankConfigPathsub : CliktCommand("Substitute variables in a path", name = "pathsub") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) - private val pathExpr by argument() - - override fun run() { - val config = talerConfig(configFile) - println(config.pathsub(pathExpr)) - } -} - -class BankConfigGet : CliktCommand("Lookup config value", name = "get") { - private val configFile by option( - "--config", "-c", - help = "set the configuration file" - ) - private val isPath by option( - "--filename", "-f", - help = "interpret value as path with dollar-expansion" - ).flag() - private val sectionName by argument() - private val optionName by argument() - - - override fun run() { - val config = talerConfig(configFile) - if (isPath) { - val res = config.lookupPath(sectionName, optionName) - if (res == null) { - logger.error("value not found in config") - exitProcess(2) - } - println(res) - } else { - val res = config.lookupString(sectionName, optionName) - if (res == null) { - logger.error("value not found in config") - exitProcess(2) - } - println(res) - } - } -} - -class BankConfigCmd : CliktCommand("Dump the configuration", name = "config") { - init { - subcommands(BankConfigDump(), BankConfigPathsub(), BankConfigGet()) - } - - override fun run() = Unit -} - class LibeufinBankCommand : CliktCommand() { init { versionOption(getVersion()) - subcommands(ServeBank(), BankDbInit(), ChangePw(), BankConfigCmd()) + subcommands(ServeBank(), BankDbInit(), ChangePw(), CliConfigCmd(BANK_CONFIG_SOURCE)) } override fun run() = Unit diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/NotificationWatcher.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/NotificationWatcher.kt @@ -24,8 +24,12 @@ import java.util.concurrent.ConcurrentHashMap import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.postgresql.ds.PGSimpleDataSource +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.util.* +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.NotificationWatcher") + /** Postgres notification collector and distributor */ internal class NotificationWatcher(private val pgSource: PGSimpleDataSource) { // ShareFlow that are manually counted for manual garbage collection diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt @@ -19,10 +19,14 @@ package tech.libeufin.bank +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.util.* import java.time.* import java.sql.Types +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.TransactionDAO") + /** Data access logic for transactions */ class TransactionDAO(private val db: Database) { /** Result status of bank transaction creation .*/ diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -378,7 +378,7 @@ fun TalerConfig.extractDbConfigOrFail(): DatabaseConfig = class LibeufinNexusCommand : CliktCommand() { init { versionOption(getVersion()) - subcommands(EbicsSetup(), DbInit(), EbicsSubmit(), EbicsFetch()) + subcommands(EbicsSetup(), DbInit(), EbicsSubmit(), EbicsFetch(), CliConfigCmd(NEXUS_CONFIG_SOURCE)) } override fun run() = Unit } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt @@ -25,6 +25,8 @@ package tech.libeufin.nexus.ebics import io.ktor.client.* import org.bouncycastle.util.encoders.UTF8 +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.nexus.BankPublicKeysFile import tech.libeufin.nexus.ClientPrivateKeysFile import tech.libeufin.nexus.EbicsSetupConfig @@ -37,6 +39,8 @@ import java.time.ZoneId import java.util.* import javax.xml.datatype.DatatypeFactory +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.nexus.Ebics2") + /** * Convenience function to download via EBICS with a * customer message type. diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsCommon.kt @@ -44,18 +44,20 @@ import io.ktor.client.statement.* import io.ktor.http.* import org.apache.commons.compress.archivers.zip.ZipFile import org.apache.commons.compress.utils.SeekableInMemoryByteChannel +import org.slf4j.Logger +import org.slf4j.LoggerFactory import tech.libeufin.nexus.* import tech.libeufin.util.* import tech.libeufin.util.ebics_h005.Ebics3Request -import tech.libeufin.util.logger import java.io.ByteArrayOutputStream import java.security.interfaces.RSAPrivateCrtKey -import java.time.Instant import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.* import java.util.zip.DeflaterInputStream +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.nexus.EbicsCommon") + /** * Available EBICS versions. */ diff --git a/util/build.gradle b/util/build.gradle @@ -35,5 +35,6 @@ dependencies { implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version") implementation("org.jetbrains.kotlin:kotlin-test:$kotlin_version") - + + implementation("com.github.ajalt.clikt:clikt:4.2.1") } \ No newline at end of file diff --git a/util/src/main/kotlin/Config.kt b/util/src/main/kotlin/Config.kt @@ -1,10 +1,7 @@ package tech.libeufin.util import ch.qos.logback.core.util.Loader -import org.slf4j.Logger -import org.slf4j.LoggerFactory -val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util") /** * Putting those values into the 'attributes' container because they * are needed by the util routines that do NOT have Sandbox and Nexus diff --git a/util/src/main/kotlin/ConfigCli.kt b/util/src/main/kotlin/ConfigCli.kt @@ -0,0 +1,119 @@ +/* + * This file is part of LibEuFin. + * Copyright (C) 2023 Taler Systems S.A. + + * LibEuFin 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. + + * LibEuFin 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 LibEuFin; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/> + */ + +package tech.libeufin.util + +import ConfigSource +import TalerConfig +import TalerConfigError +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.core.subcommands +import com.github.ajalt.clikt.parameters.arguments.argument +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import kotlin.system.exitProcess + +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.ConfigCli") + +private fun <R> catchError(lambda: () -> R): R { + try { + return lambda() + } catch (e: TalerConfigError) { + logger.error(e.message) + exitProcess(1) + } +} + +private fun talerConfig(configSource: ConfigSource, configPath: String?): TalerConfig = catchError { + val config = TalerConfig(configSource) + config.load(configPath) + config +} + +class CliConfigCmd(configSource: ConfigSource) : CliktCommand("Dump the configuration", name = "config") { + init { + subcommands(CliConfigDump(configSource), CliConfigPathsub(configSource), CliConfigGet(configSource)) + } + + override fun run() = Unit +} + +private class CliConfigGet(private val configSource: ConfigSource) : CliktCommand("Lookup config value", name = "get") { + private val configFile by option( + "--config", "-c", + help = "set the configuration file" + ) + private val isPath by option( + "--filename", "-f", + help = "interpret value as path with dollar-expansion" + ).flag() + private val sectionName by argument() + private val optionName by argument() + + + override fun run() { + val config = talerConfig(configSource, configFile) + if (isPath) { + val res = config.lookupPath(sectionName, optionName) + if (res == null) { + logger.error("value not found in config") + exitProcess(2) + } + println(res) + } else { + val res = config.lookupString(sectionName, optionName) + if (res == null) { + logger.error("value not found in config") + exitProcess(2) + } + println(res) + } + } +} + + + +private class CliConfigPathsub(private val configSource: ConfigSource) : CliktCommand("Substitute variables in a path", name = "pathsub") { + private val configFile by option( + "--config", "-c", + help = "set the configuration file" + ) + private val pathExpr by argument() + + override fun run() { + val config = talerConfig(configSource, configFile) + println(config.pathsub(pathExpr)) + } +} + +private class CliConfigDump(private val configSource: ConfigSource) : CliktCommand("Dump the configuration", name = "dump") { + private val configFile by option( + "--config", "-c", + help = "set the configuration file" + ) + + override fun run() { + val config = talerConfig(configSource, configFile) + println("# install path: ${config.getInstallPath()}") + config.load(this.configFile) + println(config.stringify()) + } +} diff --git a/util/src/main/kotlin/DB.kt b/util/src/main/kotlin/DB.kt @@ -36,6 +36,7 @@ import java.sql.SQLException fun getCurrentUser(): String = System.getProperty("user.name") +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.DB") // Check GANA (https://docs.gnunet.org/gana/index.html) for numbers allowance. diff --git a/util/src/main/kotlin/HTTP.kt b/util/src/main/kotlin/HTTP.kt @@ -4,6 +4,10 @@ import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.util.* +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.HTTP") // Get the base URL of a request, returns null if any problem occurs. fun ApplicationRequest.getBaseUrl(): String? { diff --git a/util/src/main/kotlin/IbanPayto.kt b/util/src/main/kotlin/IbanPayto.kt @@ -1,8 +1,12 @@ package tech.libeufin.util +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.net.URI import java.net.URLDecoder +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.IbanPayto") + // Payto information. data class IbanPayto( // represent query param "sender-name" or "receiver-name". diff --git a/util/src/main/kotlin/TalerConfig.kt b/util/src/main/kotlin/TalerConfig.kt @@ -17,13 +17,16 @@ * <http://www.gnu.org/licenses/> */ -import tech.libeufin.util.logger +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.File import java.nio.file.Paths import kotlin.io.path.Path import kotlin.io.path.isReadable import kotlin.io.path.listDirectoryEntries +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.TalerConfig") + private data class Section( val entries: MutableMap<String, String>, ) diff --git a/util/src/main/kotlin/UnixDomainSocket.kt b/util/src/main/kotlin/UnixDomainSocket.kt @@ -2,12 +2,9 @@ import io.ktor.client.plugins.* import io.ktor.client.request.* import io.ktor.server.application.* import io.ktor.client.statement.* -import io.ktor.http.* import io.ktor.http.HttpHeaders import io.ktor.http.HttpMethod -import io.ktor.server.engine.* import io.ktor.server.testing.* -import io.ktor.utils.io.pool.* import io.netty.bootstrap.ServerBootstrap import io.netty.buffer.ByteBufInputStream import io.netty.buffer.Unpooled @@ -20,9 +17,12 @@ import io.netty.handler.codec.http.DefaultHttpResponse import io.netty.handler.logging.LoggingHandler import io.netty.handler.stream.ChunkedStream import io.netty.handler.stream.ChunkedWriteHandler -import tech.libeufin.util.logger +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.ByteArrayInputStream +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.UnixDomainSocket") + fun startServer( unixSocketPath: String, app: Application.() -> Unit diff --git a/util/src/main/kotlin/XMLUtil.kt b/util/src/main/kotlin/XMLUtil.kt @@ -21,6 +21,8 @@ package tech.libeufin.util import com.sun.xml.bind.marshaller.NamespacePrefixMapper import io.ktor.http.* +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.w3c.dom.Document import org.w3c.dom.Node import org.w3c.dom.NodeList @@ -60,6 +62,8 @@ import javax.xml.xpath.XPath import javax.xml.xpath.XPathConstants import javax.xml.xpath.XPathFactory +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.XMLUtil") + class DefaultNamespaces : NamespacePrefixMapper() { override fun getPreferredPrefix(namespaceUri: String?, suggestion: String?, requirePrefix: Boolean): String? { if (namespaceUri == "http://www.w3.org/2000/09/xmldsig#") return "ds" diff --git a/util/src/main/kotlin/startServer.kt b/util/src/main/kotlin/startServer.kt @@ -4,8 +4,12 @@ import io.ktor.server.application.* import io.ktor.server.engine.* import io.ktor.server.netty.* import io.netty.channel.unix.Errors +import org.slf4j.Logger +import org.slf4j.LoggerFactory import kotlin.system.exitProcess +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.startServer") + const val EAFNOSUPPORT = -97 // Netty defines errors negatively. class StartServerOptions( var ipv4OnlyOpt: Boolean, diff --git a/util/src/main/kotlin/time.kt b/util/src/main/kotlin/time.kt @@ -19,10 +19,14 @@ package tech.libeufin.util +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.time.* import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit +private val logger: Logger = LoggerFactory.getLogger("tech.libeufin.util.time") + /** * Converts the 'this' Instant to the number of nanoseconds * since the Epoch. It returns the result as Long, or null