commit 83451e306b83615b70b5d5e541fdcb965862ef0e
parent 341bd2ae1c5654321f893d7e5ac2e282425184e3
Author: MS <ms@taler.net>
Date: Fri, 27 Oct 2023 12:47:53 +0200
nexus ebics-setup
only relying to the account metadata found in the
configuration, as per last DD 50 version.
Diffstat:
4 files changed, 66 insertions(+), 110 deletions(-)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSetup.kt
@@ -347,57 +347,6 @@ private fun makePdf(privs: ClientPrivateKeysFile, cfg: EbicsSetupConfig) {
}
/**
- * Extracts bank account information and stores it to the bank account
- * metadata file that's found in the configuration. It returns void in
- * case of success, or fails the whole process upon errors.
- *
- * @param cfg configuration handle.
- * @param bankAccounts bank response to EBICS HTD request.
- * @param showAssociatedAccounts if true, the function only shows the
- * users account, without persisting anything to disk.
- */
-fun extractBankAccountMetadata(
- cfg: EbicsSetupConfig,
- bankAccounts: HTDResponseOrderData,
- showAssociatedAccounts: Boolean
-) {
- // Now trying to extract whatever IBAN & BIC pair the bank gave in the response.
- val foundIban: String? = findIban(bankAccounts.partnerInfo.accountInfoList)
- val foundBic: String? = findBic(bankAccounts.partnerInfo.accountInfoList)
- // _some_ IBAN & BIC _might_ have been found, compare it with the config.
- if (foundIban == null)
- logger.warn("Bank seems NOT to show any IBAN for our account.")
- if (foundBic == null)
- logger.warn("Bank seems NOT to show any BIC for our account.")
- // Warn the user if instead one IBAN was found but that differs from the config.
- if (foundIban != null && foundIban != cfg.accountNumber) {
- logger.error("Bank has another IBAN for us: $foundIban, while config has: ${cfg.accountNumber}")
- exitProcess(1)
- }
- // Users wants to only _see_ the accounts, NOT checking values and returning here.
- if (showAssociatedAccounts) {
- println("Bank associates this account to the EBICS user ${cfg.ebicsUserId}: IBAN: $foundIban, BIC: $foundBic, Name: ${bankAccounts.userInfo.name}")
- return
- }
- // No divergences were found, either because the config was right
- // _or_ the bank didn't give any information. Setting the account
- // metadata accordingly.
- val accountMetaData = BankAccountMetadataFile(
- account_holder_name = bankAccounts.userInfo.name ?: "Account holder name not given",
- account_holder_iban = foundIban ?: run iban@ {
- logger.warn("Bank did not show any IBAN for us, defaulting to the one we configured.")
- return@iban cfg.accountNumber },
- bank_code = foundBic ?: run bic@ {
- logger.warn("Bank did not show any BIC for us, setting it as null.")
- return@bic null }
- )
- if (!syncJsonToDisk(accountMetaData, cfg.bankAccountMetadataFilename)) {
- logger.error("Failed to persist bank account meta-data at: ${cfg.bankAccountMetadataFilename}")
- exitProcess(1)
- }
-}
-
-/**
* CLI class implementing the "ebics-setup" subcommand.
*/
class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
@@ -481,7 +430,7 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
if (keysNotSub || generateRegistrationPdf) makePdf(privs, cfg)
// Checking if the bank keys exist on disk.
val bankKeysFile = File(cfg.bankPublicKeysFilename)
- if (!bankKeysFile.exists()) { // FIXME: should this also check the content validity?
+ if (!bankKeysFile.exists()) {
val areKeysOnDisk = runBlocking {
doKeysRequestAndUpdateState(
cfg,
@@ -502,43 +451,24 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") {
logger.error("Although previous checks, could not load the bank keys file from: ${cfg.bankPublicKeysFilename}")
exitProcess(1)
}
- /**
- * The following block potentially updates the bank keys state
- * on disk, if that's the first time that they become accepted.
- * If so, finally reloads the bank keys file from disk.
- */
- val bankKeys = if (!bankKeysMaybe.accepted) {
-
- if (autoAcceptKeys) bankKeysMaybe.accepted = true
- else bankKeysMaybe.accepted = askUserToAcceptKeys(bankKeysMaybe)
-
- if (!bankKeysMaybe.accepted) {
- logger.error("Cannot continue without accepting the bank keys.")
- exitProcess(1)
- }
+ val printOk = { println("setup ready") }
- if (!syncJsonToDisk(bankKeysMaybe, cfg.bankPublicKeysFilename)) {
- logger.error("Could not set bank keys as accepted on disk.")
- exitProcess(1)
- }
- // Reloading after the disk write above.
- loadBankKeys(cfg.bankPublicKeysFilename) ?: kotlin.run {
- logger.error("Could not reload bank keys after disk write.")
- exitProcess(1)
- }
- } else
- bankKeysMaybe // keys were already accepted.
+ if (bankKeysMaybe.accepted) {
+ printOk()
+ return
+ }
+ // Finishing the setup by accepting the bank keys.
+ if (autoAcceptKeys) bankKeysMaybe.accepted = true
+ else bankKeysMaybe.accepted = askUserToAcceptKeys(bankKeysMaybe)
- // Downloading the list of owned bank account(s).
- val bankAccounts = runBlocking {
- fetchBankAccounts(cfg, privs, bankKeys, httpClient)
+ if (!bankKeysMaybe.accepted) {
+ logger.error("Cannot successfully finish the setup without accepting the bank keys.")
+ exitProcess(1)
}
- if (bankAccounts == null) {
- logger.error("Could not obtain the list of bank accounts from the bank.")
+ if (!syncJsonToDisk(bankKeysMaybe, cfg.bankPublicKeysFilename)) {
+ logger.error("Could not set bank keys as accepted on disk.")
exitProcess(1)
}
- logger.info("Subscriber's bank accounts fetched.")
- extractBankAccountMetadata(cfg, bankAccounts, showAssociatedAccounts)
- println("setup ready")
+ printOk()
}
}
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -101,10 +101,14 @@ class EbicsSetupConfig(val config: TalerConfig) {
*/
val ebicsSystemId = ebicsSetupRequireString("system_id")
/**
- * Bank account name, as given by the bank. It
- * can be an IBAN or even any alphanumeric value.
+ * Bank account metadata.
*/
- val accountNumber = ebicsSetupRequireString("account_number")
+ val accountNumber: IbanPayto = ebicsSetupRequireString("account_number").run {
+ val maybeAccount = parsePayto(this)
+ if (maybeAccount?.bic == null) throw Exception("ACCOUNT_NUMBER lacks the BIC")
+ if (maybeAccount.receiverName == null) throw Exception("ACCOUNT_NUMBER lacks the name")
+ return@run maybeAccount
+ }
/**
* Filename where we store the bank public keys.
*/
@@ -114,10 +118,6 @@ class EbicsSetupConfig(val config: TalerConfig) {
*/
val clientPrivateKeysFilename = ebicsSetupRequireString("client_private_keys_file")
/**
- * Filename where we store the bank account main information.
- */
- val bankAccountMetadataFilename = ebicsSetupRequireString("account_meta_data_file")
- /**
* A name that identifies the EBICS and ISO20022 flavour
* that Nexus should honor in the communication with the
* bank.
@@ -201,23 +201,6 @@ data class BankPublicKeysFile(
)
/**
- * Gets the bank account metadata file, according to the
- * location found in the configuration. The caller may still
- * have to handle the exception, in case the found file doesn't
- * parse to the wanted JSON type.
- *
- * @param cfg configuration handle.
- * @return [BankAccountMetadataFile] or null, if the file wasn't found.
- */
-fun loadBankAccountFile(cfg: EbicsSetupConfig): BankAccountMetadataFile? {
- val f = File(cfg.bankAccountMetadataFilename)
- if (!f.exists()) {
- logger.error("Bank account metadata file not found in ${cfg.bankAccountMetadataFilename}")
- return null
- }
- return myJson.decodeFromString(f.readText())
-}
-/**
* Load the bank keys file from disk.
*
* @param location the keys file location.
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/Ebics2.kt
@@ -35,6 +35,30 @@ import java.util.*
import javax.xml.datatype.DatatypeFactory
/**
+ * Convenience function to download via EBICS with a
+ * customer message type.
+ *
+ * @param messageType EBICS 2.x message type. Defaults
+ * to HTD, to get general information about the EBICS
+ * subscriber.
+ * @param cfg configuration handle
+ * @param clientKeys client EBICS keys.
+ * @param bankKeys bank EBICS keys.
+ * @param client HTTP client handle.
+ * @return raw XML response, or null upon errors.
+ */
+suspend fun doEbicsCustomDownload(
+ messageType: String = "HTD",
+ cfg: EbicsSetupConfig,
+ clientKeys: ClientPrivateKeysFile,
+ bankKeys: BankPublicKeysFile,
+ client: HttpClient
+): String? {
+ val xmlReq = createEbics25DownloadInit(cfg, clientKeys, bankKeys, messageType)
+ return doEbicsDownload(client, cfg, clientKeys, bankKeys, xmlReq, false)
+}
+
+/**
* Request EBICS (2.x) HTD to the bank. This message type
* gets the list of bank accounts that are owned by the EBICS
* client.
@@ -59,6 +83,7 @@ suspend fun fetchBankAccounts(
return null
}
return try {
+ logger.debug("Fetched accounts: $xmlResp")
XMLUtil.convertStringToJaxb<HTDResponseOrderData>(xmlResp).value
} catch (e: Exception) {
logger.error("Could not parse the HTD payload, detail: ${e.message}")
diff --git a/nexus/src/test/kotlin/PostFinance.kt b/nexus/src/test/kotlin/PostFinance.kt
@@ -3,6 +3,7 @@ import kotlinx.coroutines.runBlocking
import org.junit.Ignore
import org.junit.Test
import tech.libeufin.nexus.*
+import tech.libeufin.nexus.ebics.doEbicsCustomDownload
import tech.libeufin.nexus.ebics.fetchBankAccounts
import tech.libeufin.nexus.ebics.submitPayment
import tech.libeufin.util.IbanPayto
@@ -50,7 +51,7 @@ class Iso20022 {
}
}
-@Ignore
+// @Ignore
class PostFinance {
// Tests sending client keys to the PostFinance test platform.
@Test
@@ -83,6 +84,23 @@ class PostFinance {
}
}
+ @Test
+ fun customDownload() {
+ val cfg = prep()
+ val clientKeys = loadPrivateKeysFromDisk(cfg.clientPrivateKeysFilename)
+ val bankKeys = loadBankKeys(cfg.bankPublicKeysFilename)
+ runBlocking {
+ val xml = doEbicsCustomDownload(
+ messageType = "HTD",
+ cfg = cfg,
+ bankKeys = bankKeys!!,
+ clientKeys = clientKeys!!,
+ client = HttpClient()
+ )
+ println(xml)
+ }
+ }
+
// Tests the HTD message type.
@Test
fun fetchAccounts() {