summaryrefslogtreecommitdiff
path: root/nexus/src/main
diff options
context:
space:
mode:
authorMarcello Stanisci <ms@taler.net>2020-04-28 15:03:46 +0200
committerMarcello Stanisci <ms@taler.net>2020-04-28 15:03:46 +0200
commit55a63c220d9af923aa986138f27bdcea7be36768 (patch)
treed1d85015bb2e10eeaf7251e2fc5af0af7da3a2c6 /nexus/src/main
parent8fbb4e5ae496c0c046552b25bc27fbf91c52eb2c (diff)
downloadlibeufin-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.kt272
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt272
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() {