diff options
author | Marcello Stanisci <ms@taler.net> | 2020-04-28 15:03:46 +0200 |
---|---|---|
committer | Marcello Stanisci <ms@taler.net> | 2020-04-28 15:03:46 +0200 |
commit | 55a63c220d9af923aa986138f27bdcea7be36768 (patch) | |
tree | d1d85015bb2e10eeaf7251e2fc5af0af7da3a2c6 /nexus/src/main | |
parent | 8fbb4e5ae496c0c046552b25bc27fbf91c52eb2c (diff) | |
download | libeufin-55a63c220d9af923aa986138f27bdcea7be36768.tar.gz libeufin-55a63c220d9af923aa986138f27bdcea7be36768.tar.bz2 libeufin-55a63c220d9af923aa986138f27bdcea7be36768.zip |
Remove many helpers from main nexus file.
Diffstat (limited to 'nexus/src/main')
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 272 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 272 |
2 files changed, 271 insertions, 273 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt index 7462d31d..bdde8977 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt @@ -2,14 +2,284 @@ package tech.libeufin.nexus import io.ktor.application.ApplicationCall import io.ktor.http.HttpStatusCode -import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.transactions.transaction import org.joda.time.DateTime import org.joda.time.format.DateTimeFormat +import tech.libeufin.util.Amount import tech.libeufin.util.CryptoUtil +import tech.libeufin.util.EbicsClientSubscriberDetails import tech.libeufin.util.base64ToBytes import javax.sql.rowset.serial.SerialBlob +import java.util.Random +import tech.libeufin.util.ebics_h004.EbicsTypes +import java.security.interfaces.RSAPublicKey +import tech.libeufin.util.* +import java.time.format.DateTimeFormatter +import java.time.ZonedDateTime +import java.time.Instant +import java.time.ZoneId + +fun getSubscriberEntityFromNexusUserId(nexusUserId: String): EbicsSubscriberEntity { + return transaction { + val nexusUser = expectNexusIdTransaction(nexusUserId) + nexusUser.ebicsSubscriber + } +} + +fun calculateRefund(amount: String): Amount { + // fixme: must apply refund fees! + return Amount(amount) +} + +/** + * Skip national only-numeric bank account ids, and return the first IBAN in list + */ +fun extractFirstIban(bankAccounts: List<EbicsTypes.AbstractAccountNumber>?): String? { + if (bankAccounts == null) + return null + + for (item in bankAccounts) { + if (item is EbicsTypes.GeneralAccountNumber) { + if (item.international) + return item.value + } + } + return null +} + +/** + * Skip national only-numeric codes, and returns the first BIC in list + */ +fun extractFirstBic(bankCodes: List<EbicsTypes.AbstractBankCode>?): String? { + if (bankCodes == null) + return null + + for (item in bankCodes) { + if (item is EbicsTypes.GeneralBankCode) { + if (item.international) + return item.value + } + } + return null +} + +/** + * Get EBICS subscriber details from bank account id. + * bank account id => ... => ebics details + */ +fun getSubscriberDetailsFromBankAccount(bankAccountId: String): EbicsClientSubscriberDetails { + return transaction { + val map = EbicsToBankAccountEntity.find { + EbicsToBankAccountsTable.bankAccount eq bankAccountId + }.firstOrNull() ?: throw NexusError( + HttpStatusCode.NotFound, + "Such bank account '$bankAccountId' has no EBICS subscriber associated" + ) + getSubscriberDetailsInternal(map.ebicsSubscriber) + } +} + +/** + * Given a nexus user id, returns the _list_ of bank accounts associated to it. + * + * @param id the subscriber id + * @return the bank account associated with this user. Can/should be adapted to + * return multiple bank accounts. + */ +fun getBankAccountFromNexusUserId(id: String): BankAccountEntity { + logger.debug("Looking up bank account of user '$id'") + val map = transaction { + UserToBankAccountEntity.find { + UserToBankAccountsTable.nexusUser eq id + } + }.firstOrNull() ?: throw NexusError( + HttpStatusCode.NotFound, + "Such user '$id' does not have any bank account associated" + ) + return map.bankAccount +} + +fun getSubscriberDetailsInternal(subscriber: EbicsSubscriberEntity): EbicsClientSubscriberDetails { + var bankAuthPubValue: RSAPublicKey? = null + if (subscriber.bankAuthenticationPublicKey != null) { + bankAuthPubValue = CryptoUtil.loadRsaPublicKey( + subscriber.bankAuthenticationPublicKey?.toByteArray()!! + ) + } + var bankEncPubValue: RSAPublicKey? = null + if (subscriber.bankEncryptionPublicKey != null) { + bankEncPubValue = CryptoUtil.loadRsaPublicKey( + subscriber.bankEncryptionPublicKey?.toByteArray()!! + ) + } + return EbicsClientSubscriberDetails( + bankAuthPub = bankAuthPubValue, + bankEncPub = bankEncPubValue, + + ebicsUrl = subscriber.ebicsURL, + hostId = subscriber.hostID, + userId = subscriber.userID, + partnerId = subscriber.partnerID, + + customerSignPriv = CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray()), + customerAuthPriv = CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.toByteArray()), + customerEncPriv = CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.toByteArray()) + ) +} + +fun getSubscriberDetailsFromNexusUserId(id: String): EbicsClientSubscriberDetails { + return transaction { + val nexusUser = expectNexusIdTransaction(id) + getSubscriberDetailsInternal(nexusUser.ebicsSubscriber) + } +} + +/** + * Create a PAIN.001 XML document according to the input data. + * Needs to be called within a transaction block. + */ +fun createPain001document(pain001Entity: Pain001Entity): String { + /** + * Every PAIN.001 document contains at least three IDs: + * + * 1) MsgId: a unique id for the message itself + * 2) PmtInfId: the unique id for the payment's set of information + * 3) EndToEndId: a unique id to be shared between the debtor and + * creditor that uniquely identifies the transaction + * + * For now and for simplicity, since every PAIN entry in the database + * has a unique ID, and the three values aren't required to be mutually different, + * we'll assign the SAME id (= the row id) to all the three aforementioned + * PAIN id types. + */ + + val s = constructXml(indent = true) { + root("Document") { + attribute("xmlns", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03") + attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") + attribute("xsi:schemaLocation", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03 pain.001.001.03.xsd") + element("CstmrCdtTrfInitn") { + element("GrpHdr") { + element("MsgId") { + text(pain001Entity.id.value.toString()) + } + element("CreDtTm") { + val dateMillis = transaction { + pain001Entity.date + } + val dateFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME + val instant = Instant.ofEpochSecond(dateMillis / 1000) + val zoned = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()) + text(dateFormatter.format(zoned)) + } + element("NbOfTxs") { + text("1") + } + element("CtrlSum") { + text(pain001Entity.sum.toString()) + } + element("InitgPty/Nm") { + text(pain001Entity.debtorAccount) + } + } + element("PmtInf") { + element("PmtInfId") { + text(pain001Entity.id.value.toString()) + } + element("PmtMtd") { + text("TRF") + } + element("BtchBookg") { + text("true") + } + element("NbOfTxs") { + text("1") + } + element("CtrlSum") { + text(pain001Entity.sum.toString()) + } + element("PmtTpInf/SvcLvl/Cd") { + text("SEPA") + } + element("ReqdExctnDt") { + val dateMillis = transaction { + pain001Entity.date + } + text(DateTime(dateMillis).toString("Y-MM-dd")) + } + element("Dbtr/Nm") { + text(pain001Entity.debtorAccount) + } + element("DbtrAcct/Id/IBAN") { + text(transaction { + BankAccountEntity.findById(pain001Entity.debtorAccount)?.iban ?: throw NexusError(HttpStatusCode.NotFound,"Debtor IBAN not found in database") + }) + } + element("DbtrAgt/FinInstnId/BIC") { + + text(transaction { + BankAccountEntity.findById(pain001Entity.debtorAccount)?.bankCode ?: throw NexusError(HttpStatusCode.NotFound,"Debtor BIC not found in database") + }) + } + element("ChrgBr") { + text("SLEV") + } + element("CdtTrfTxInf") { + element("PmtId") { + element("EndToEndId") { + // text(pain001Entity.id.value.toString()) + text("NOTPROVIDED") + } + } + element("Amt/InstdAmt") { + attribute("Ccy", pain001Entity.currency) + text(pain001Entity.sum.toString()) + } + element("CdtrAgt/FinInstnId/BIC") { + text(pain001Entity.creditorBic) + } + element("Cdtr/Nm") { + text(pain001Entity.creditorName) + } + element("CdtrAcct/Id/IBAN") { + text(pain001Entity.creditorIban) + } + element("RmtInf/Ustrd") { + text(pain001Entity.subject) + } + } + } + } + } + } + return s +} + +/** + * Insert one row in the database, and leaves it marked as non-submitted. + * @param debtorAccountId the mnemonic id assigned by the bank to one bank + * account of the subscriber that is creating the pain entity. In this case, + * it will be the account whose money will pay the wire transfer being defined + * by this pain document. + */ +fun createPain001entity(entry: Pain001Data, debtorAccountId: String): Pain001Entity { + val randomId = Random().nextLong() + return transaction { + Pain001Entity.new { + subject = entry.subject + sum = entry.sum + debtorAccount = debtorAccountId + creditorName = entry.creditorName + creditorBic = entry.creditorBic + creditorIban = entry.creditorIban + date = DateTime.now().millis + paymentId = randomId + msgId = randomId + endToEndId = randomId + } + } +} /** * Inserts spaces every 2 characters, and a newline after 8 pairs. diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt index 288f878d..201367c1 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -43,27 +43,16 @@ import kotlinx.coroutines.io.ByteReadChannel import kotlinx.coroutines.io.jvm.javaio.toByteReadChannel import kotlinx.coroutines.io.jvm.javaio.toInputStream import kotlinx.io.core.ExperimentalIoApi -import org.jetbrains.exposed.sql.SizedIterable -import org.jetbrains.exposed.sql.StdOutSqlLogger -import org.jetbrains.exposed.sql.addLogger import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.transactions.transaction -import org.joda.time.DateTime import org.slf4j.Logger import org.slf4j.LoggerFactory import org.slf4j.event.Level import tech.libeufin.util.* -import tech.libeufin.util.ebics_h004.EbicsTypes import tech.libeufin.util.ebics_h004.HTDResponseOrderData -import java.security.interfaces.RSAPublicKey import java.text.DateFormat import java.text.SimpleDateFormat -import java.time.Instant -import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter import java.util.* -import java.util.zip.Inflater import java.util.zip.InflaterInputStream import javax.crypto.EncryptedPrivateKeyInfo import javax.sql.rowset.serial.SerialBlob @@ -77,267 +66,6 @@ fun isProduction(): Boolean { return System.getenv("NEXUS_PRODUCTION") != null } -fun getSubscriberEntityFromNexusUserId(nexusUserId: String): EbicsSubscriberEntity { - return transaction { - val nexusUser = expectNexusIdTransaction(nexusUserId) - nexusUser.ebicsSubscriber - } -} - -fun calculateRefund(amount: String): Amount { - // fixme: must apply refund fees! - return Amount(amount) -} - -/** - * Skip national only-numeric bank account ids, and return the first IBAN in list - */ -fun extractFirstIban(bankAccounts: List<EbicsTypes.AbstractAccountNumber>?): String? { - if (bankAccounts == null) - return null - - for (item in bankAccounts) { - if (item is EbicsTypes.GeneralAccountNumber) { - if (item.international) - return item.value - } - } - return null -} - -/** - * Skip national only-numeric codes, and returns the first BIC in list - */ -fun extractFirstBic(bankCodes: List<EbicsTypes.AbstractBankCode>?): String? { - if (bankCodes == null) - return null - - for (item in bankCodes) { - if (item is EbicsTypes.GeneralBankCode) { - if (item.international) - return item.value - } - } - return null -} - -/** - * Get EBICS subscriber details from bank account id. - * bank account id => ... => ebics details - */ -fun getSubscriberDetailsFromBankAccount(bankAccountId: String): EbicsClientSubscriberDetails { - return transaction { - val map = EbicsToBankAccountEntity.find { - EbicsToBankAccountsTable.bankAccount eq bankAccountId - }.firstOrNull() ?: throw NexusError( - HttpStatusCode.NotFound, - "Such bank account '$bankAccountId' has no EBICS subscriber associated" - ) - getSubscriberDetailsInternal(map.ebicsSubscriber) - } -} - -/** - * Given a nexus user id, returns the _list_ of bank accounts associated to it. - * - * @param id the subscriber id - * @return the bank account associated with this user. Can/should be adapted to - * return multiple bank accounts. - */ -fun getBankAccountFromNexusUserId(id: String): BankAccountEntity { - logger.debug("Looking up bank account of user '$id'") - val map = transaction { - UserToBankAccountEntity.find { - UserToBankAccountsTable.nexusUser eq id - } - }.firstOrNull() ?: throw NexusError( - HttpStatusCode.NotFound, - "Such user '$id' does not have any bank account associated" - ) - return map.bankAccount -} - -fun getSubscriberDetailsInternal(subscriber: EbicsSubscriberEntity): EbicsClientSubscriberDetails { - var bankAuthPubValue: RSAPublicKey? = null - if (subscriber.bankAuthenticationPublicKey != null) { - bankAuthPubValue = CryptoUtil.loadRsaPublicKey( - subscriber.bankAuthenticationPublicKey?.toByteArray()!! - ) - } - var bankEncPubValue: RSAPublicKey? = null - if (subscriber.bankEncryptionPublicKey != null) { - bankEncPubValue = CryptoUtil.loadRsaPublicKey( - subscriber.bankEncryptionPublicKey?.toByteArray()!! - ) - } - return EbicsClientSubscriberDetails( - bankAuthPub = bankAuthPubValue, - bankEncPub = bankEncPubValue, - - ebicsUrl = subscriber.ebicsURL, - hostId = subscriber.hostID, - userId = subscriber.userID, - partnerId = subscriber.partnerID, - - customerSignPriv = CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray()), - customerAuthPriv = CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.toByteArray()), - customerEncPriv = CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.toByteArray()) - ) -} - -fun getSubscriberDetailsFromNexusUserId(id: String): EbicsClientSubscriberDetails { - return transaction { - val nexusUser = expectNexusIdTransaction(id) - getSubscriberDetailsInternal(nexusUser.ebicsSubscriber) - } -} - -/** - * Create a PAIN.001 XML document according to the input data. - * Needs to be called within a transaction block. - */ -fun createPain001document(pain001Entity: Pain001Entity): String { - /** - * Every PAIN.001 document contains at least three IDs: - * - * 1) MsgId: a unique id for the message itself - * 2) PmtInfId: the unique id for the payment's set of information - * 3) EndToEndId: a unique id to be shared between the debtor and - * creditor that uniquely identifies the transaction - * - * For now and for simplicity, since every PAIN entry in the database - * has a unique ID, and the three values aren't required to be mutually different, - * we'll assign the SAME id (= the row id) to all the three aforementioned - * PAIN id types. - */ - - val s = constructXml(indent = true) { - root("Document") { - attribute("xmlns", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03") - attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") - attribute("xsi:schemaLocation", "urn:iso:std:iso:20022:tech:xsd:pain.001.001.03 pain.001.001.03.xsd") - element("CstmrCdtTrfInitn") { - element("GrpHdr") { - element("MsgId") { - text(pain001Entity.id.value.toString()) - } - element("CreDtTm") { - val dateMillis = transaction { - pain001Entity.date - } - val dateFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME - val instant = Instant.ofEpochSecond(dateMillis / 1000) - val zoned = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()) - text(dateFormatter.format(zoned)) - } - element("NbOfTxs") { - text("1") - } - element("CtrlSum") { - text(pain001Entity.sum.toString()) - } - element("InitgPty/Nm") { - text(pain001Entity.debtorAccount) - } - } - element("PmtInf") { - element("PmtInfId") { - text(pain001Entity.id.value.toString()) - } - element("PmtMtd") { - text("TRF") - } - element("BtchBookg") { - text("true") - } - element("NbOfTxs") { - text("1") - } - element("CtrlSum") { - text(pain001Entity.sum.toString()) - } - element("PmtTpInf/SvcLvl/Cd") { - text("SEPA") - } - element("ReqdExctnDt") { - val dateMillis = transaction { - pain001Entity.date - } - text(DateTime(dateMillis).toString("Y-MM-dd")) - } - element("Dbtr/Nm") { - text(pain001Entity.debtorAccount) - } - element("DbtrAcct/Id/IBAN") { - text(transaction { - BankAccountEntity.findById(pain001Entity.debtorAccount)?.iban ?: throw NexusError(HttpStatusCode.NotFound,"Debtor IBAN not found in database") - }) - } - element("DbtrAgt/FinInstnId/BIC") { - - text(transaction { - BankAccountEntity.findById(pain001Entity.debtorAccount)?.bankCode ?: throw NexusError(HttpStatusCode.NotFound,"Debtor BIC not found in database") - }) - } - element("ChrgBr") { - text("SLEV") - } - element("CdtTrfTxInf") { - element("PmtId") { - element("EndToEndId") { - // text(pain001Entity.id.value.toString()) - text("NOTPROVIDED") - } - } - element("Amt/InstdAmt") { - attribute("Ccy", pain001Entity.currency) - text(pain001Entity.sum.toString()) - } - element("CdtrAgt/FinInstnId/BIC") { - text(pain001Entity.creditorBic) - } - element("Cdtr/Nm") { - text(pain001Entity.creditorName) - } - element("CdtrAcct/Id/IBAN") { - text(pain001Entity.creditorIban) - } - element("RmtInf/Ustrd") { - text(pain001Entity.subject) - } - } - } - } - } - } - return s -} - -/** - * Insert one row in the database, and leaves it marked as non-submitted. - * @param debtorAccountId the mnemonic id assigned by the bank to one bank - * account of the subscriber that is creating the pain entity. In this case, - * it will be the account whose money will pay the wire transfer being defined - * by this pain document. - */ -fun createPain001entity(entry: Pain001Data, debtorAccountId: String): Pain001Entity { - val randomId = Random().nextLong() - return transaction { - Pain001Entity.new { - subject = entry.subject - sum = entry.sum - debtorAccount = debtorAccountId - creditorName = entry.creditorName - creditorBic = entry.creditorBic - creditorIban = entry.creditorIban - date = DateTime.now().millis - paymentId = randomId - msgId = randomId - endToEndId = randomId - } - } -} - @ExperimentalIoApi @KtorExperimentalAPI fun main() { |