diff options
author | MS <ms@taler.net> | 2020-05-14 17:47:23 +0200 |
---|---|---|
committer | MS <ms@taler.net> | 2020-05-14 17:47:23 +0200 |
commit | 8f118987a6fe292e1fd5a7d16fe0baf63a5f3240 (patch) | |
tree | 623e03f29a9c2b0d99ccdcafd76a898ddc618615 /nexus/src/main | |
parent | 24d03f3e49848a2109d02ad9b6d19ad72a633dad (diff) | |
download | libeufin-8f118987a6fe292e1fd5a7d16fe0baf63a5f3240.tar.gz libeufin-8f118987a6fe292e1fd5a7d16fe0baf63a5f3240.tar.bz2 libeufin-8f118987a6fe292e1fd5a7d16fe0baf63a5f3240.zip |
Overloading POST ../bank-transport.
Plus getting the Taler facade to compile again.
Diffstat (limited to 'nexus/src/main')
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 21 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt | 10 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 159 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 86 |
4 files changed, 154 insertions, 122 deletions
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt index 93e5eb94..135d53ba 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt @@ -1,5 +1,7 @@ package tech.libeufin.nexus +import com.google.gson.Gson +import com.google.gson.JsonObject import io.ktor.client.HttpClient import io.ktor.http.HttpStatusCode import org.jetbrains.exposed.sql.and @@ -60,6 +62,12 @@ fun extractFirstBic(bankCodes: List<EbicsTypes.AbstractBankCode>?): String? { return null } +fun getTransportFromJsonObject(jo: JsonObject): Transport { + return Gson().fromJson( + expectNonNull(jo.get("transport")).asJsonObject, Transport::class.java + ) +} + /** * Retrieve bank account details, only if user owns it. */ @@ -128,7 +136,7 @@ fun getEbicsSubscriberDetailsInternal(subscriber: EbicsSubscriberEntity): EbicsC ) } -fun getEbicsTransport(userId: String, transportId: String?): EbicsSubscriberEntity { +fun getEbicsTransport(userId: String, transportId: String? = null): EbicsSubscriberEntity { val transport = transaction { if (transportId == null) { return@transaction EbicsSubscriberEntity.all().first() @@ -150,7 +158,7 @@ fun getEbicsTransport(userId: String, transportId: String?): EbicsSubscriberEnti /** * Retrieve Ebics subscriber details given a Transport - * object and handling the default case. + * object and handling the default case (when this latter is null). */ fun getEbicsSubscriberDetails(userId: String, transportId: String?): EbicsClientSubscriberDetails { val transport = getEbicsTransport(userId, transportId) @@ -370,6 +378,15 @@ fun getPreparedPayment(uuid: String): PreparedPaymentEntity { ) } +fun getNexusUser(id: String): NexusUserEntity { + return transaction { + NexusUserEntity.findById(id) + } ?: throw NexusError( + HttpStatusCode.NotFound, + "User '$id' not found" + ) +} + /** * 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 diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt index 2a8f72d2..8f27ec94 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt @@ -1,8 +1,6 @@ package tech.libeufin.nexus -import com.sun.jdi.connect.Transport import tech.libeufin.util.* -import java.lang.NullPointerException import java.time.LocalDate data class EbicsBackupRequestJson( @@ -79,10 +77,10 @@ data class RawPayments( /************************************************* * API types (used as requests/responses types) * *************************************************/ -data class BankTransport<T, S>( - val transport: tech.libeufin.nexus.Transport, - val backup: T?, - val data: S? +data class BankTransport( + val transport: Transport, + val backup: Any? = null, + val data: Any? ) /** diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt index 488dce7b..43db3b26 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -19,6 +19,8 @@ package tech.libeufin.nexus +import com.google.gson.Gson +import com.google.gson.JsonObject import io.ktor.application.ApplicationCallPipeline import io.ktor.application.call import io.ktor.application.install @@ -404,88 +406,99 @@ fun main() { post("/bank-transports") { val userId = authenticateRequest(call.request.headers["Authorization"]) // user exists and is authenticated. - val body = call.receive<BankTransport>() - logger.debug("Request body parsed") - when (body.backup) { - is EbicsKeysBackupJson -> { - logger.debug("Restoring a transport: ${body.transport.name}") - val (authKey, encKey, sigKey) = try { - Triple( - CryptoUtil.decryptKey( - EncryptedPrivateKeyInfo(base64ToBytes(body.backup.authBlob)), - body.backup.passphrase - ), - CryptoUtil.decryptKey( - EncryptedPrivateKeyInfo(base64ToBytes(body.backup.encBlob)), - body.backup.passphrase - ), - CryptoUtil.decryptKey( - EncryptedPrivateKeyInfo(base64ToBytes(body.backup.sigBlob)), - body.backup.passphrase - ) - ) - } catch (e: Exception) { - e.printStackTrace() - logger.info("Restoring keys failed, probably due to wrong passphrase") - throw NexusError( - HttpStatusCode.BadRequest, - "Bad backup given" + val body = call.receive<JsonObject>() + val transport: Transport = getTransportFromJsonObject(body) + when (transport.type) { + "ebics" -> { + if (body.get("backup") != null) { + val backup = Gson().fromJson( + body.get("backup").asJsonObject, + EbicsKeysBackupJson::class.java ) + val (authKey, encKey, sigKey) = try { + Triple( + CryptoUtil.decryptKey( + EncryptedPrivateKeyInfo(base64ToBytes(backup.authBlob)), + backup.passphrase + ), + CryptoUtil.decryptKey( + EncryptedPrivateKeyInfo(base64ToBytes(backup.encBlob)), + backup.passphrase + ), + CryptoUtil.decryptKey( + EncryptedPrivateKeyInfo(base64ToBytes(backup.sigBlob)), + backup.passphrase + ) + ) + } catch (e: Exception) { + e.printStackTrace() + logger.info("Restoring keys failed, probably due to wrong passphrase") + throw NexusError( + HttpStatusCode.BadRequest, + "Bad backup given" + ) + } + logger.info("Restoring keys, creating new user: $userId") + try { + transaction { + EbicsSubscriberEntity.new(transport.name) { + this.nexusUser = extractNexusUser(userId) + ebicsURL = backup.ebicsURL + hostID = backup.hostID + partnerID = backup.partnerID + userID = backup.userID + signaturePrivateKey = SerialBlob(sigKey.encoded) + encryptionPrivateKey = SerialBlob(encKey.encoded) + authenticationPrivateKey = SerialBlob(authKey.encoded) + } + } + } catch (e: Exception) { + print(e) + call.respond( + NexusErrorJson("Could not store the new account into database") + ) + return@post + } + call.respondText("Backup restored") + + return@post } - logger.info("Restoring keys, creating new user: $userId") - try { + if (body.get("data") != null) { + val data = Gson().fromJson( + body.get("data"), + EbicsNewTransport::class.java + ) + val pairA = CryptoUtil.generateRsaKeyPair(2048) + val pairB = CryptoUtil.generateRsaKeyPair(2048) + val pairC = CryptoUtil.generateRsaKeyPair(2048) transaction { - EbicsSubscriberEntity.new(body.transport.name) { - this.nexusUser = extractNexusUser(userId) - ebicsURL = body.backup.ebicsURL - hostID = body.backup.hostID - partnerID = body.backup.partnerID - userID = body.backup.userID - signaturePrivateKey = SerialBlob(sigKey.encoded) - encryptionPrivateKey = SerialBlob(encKey.encoded) - authenticationPrivateKey = SerialBlob(authKey.encoded) + EbicsSubscriberEntity.new(transport.name) { + nexusUser = extractNexusUser(userId) + ebicsURL = data.ebicsURL + hostID = data.hostID + partnerID = data.partnerID + userID = data.userID + systemID = data.systemID + signaturePrivateKey = SerialBlob(pairA.private.encoded) + encryptionPrivateKey = SerialBlob(pairB.private.encoded) + authenticationPrivateKey = SerialBlob(pairC.private.encoded) } } - } catch (e: Exception) { - print(e) - call.respond( - NexusErrorJson("Could not store the new account into database") - ) + call.respondText("EBICS user successfully created") return@post } - call.respondText("Backup restored") - return@post + throw NexusError( + HttpStatusCode.BadRequest, + "Neither restore or new transport were specified." + ) } - } - when (body.data) { - is EbicsNewTransport -> { - logger.debug("Creating new transport: ${body.transport.name}") - val pairA = CryptoUtil.generateRsaKeyPair(2048) - val pairB = CryptoUtil.generateRsaKeyPair(2048) - val pairC = CryptoUtil.generateRsaKeyPair(2048) - transaction { - EbicsSubscriberEntity.new(body.transport.name) { - nexusUser = extractNexusUser(userId) - ebicsURL = body.data.ebicsURL - hostID = body.data.hostID - partnerID = body.data.partnerID - userID = body.data.userID - systemID = body.data.systemID - signaturePrivateKey = SerialBlob(pairA.private.encoded) - encryptionPrivateKey = SerialBlob(pairB.private.encoded) - authenticationPrivateKey = SerialBlob(pairC.private.encoded) - } - } - call.respondText("EBICS user successfully created") - return@post + else -> { + throw NexusError( + HttpStatusCode.BadRequest, + "Invalid transport type '${transport.type}'" + ) } - } // end of second 'when'. - - call.respond( - HttpStatusCode.BadRequest, - "Neither backup nor new transport given in request" - ) - return@post + } } /** * Sends to the bank a message "MSG" according to the transport diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt index b259bb51..f3dd6148 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt @@ -226,7 +226,7 @@ class Taler(app: Route) { val creditorObj = parsePayto(transferRequest.credit_account) val opaque_row_id = transaction { val creditorData = parsePayto(transferRequest.credit_account) - val exchangeBankAccount = getBankAccountFromNexusUserId(exchangeId) + val exchangeBankAccount = getBankAccountsFromNexusUserId(exchangeId).first() val nexusUser = extractNexusUser(exchangeId) /** Checking the UID has the desired characteristics */ TalerRequestedPaymentEntity.find { @@ -251,9 +251,7 @@ class Taler(app: Route) { subject = transferRequest.wtid, sum = amountObj.amount, currency = amountObj.currency, - debitorName = exchangeBankAccount.accountHolder, - debitorBic = exchangeBankAccount.bankCode, - debitorIban = exchangeBankAccount.iban + debitorAccount = exchangeBankAccount.id.value ), nexusUser ) @@ -264,11 +262,10 @@ class Taler(app: Route) { transactionType = "DBIT" currency = amountObj.currency this.amount = amountObj.amount.toPlainString() - debitorName = "Exchange Company" - debitorIban = exchangeBankAccount.iban - creditorName = creditorObj.name - creditorIban = creditorObj.iban counterpartBic = creditorObj.bic + counterpartIban = creditorObj.iban + counterpartName = creditorObj.name + bankAccount = exchangeBankAccount bookingDate = DateTime.now().millis this.nexusUser = nexusUser status = "BOOK" @@ -312,21 +309,20 @@ class Taler(app: Route) { val debtor = parsePayto(addIncomingData.debit_account) val amount = parseAmount(addIncomingData.amount) val (bookingDate, opaque_row_id) = transaction { - val exchangeBankAccount = getBankAccountFromNexusUserId(exchangeId) + val exchangeBankAccount = getBankAccountsFromNexusUserId(exchangeId).first() val rawPayment = RawBankTransactionEntity.new { sourceFileName = "test" unstructuredRemittanceInformation = addIncomingData.reserve_pub transactionType = "CRDT" currency = amount.currency this.amount = amount.amount.toPlainString() - creditorIban = exchangeBankAccount.iban - creditorName = exchangeBankAccount.accountHolder - debitorIban = debtor.iban - debitorName = debtor.name counterpartBic = debtor.bic + counterpartName = debtor.name + counterpartIban = debtor.iban bookingDate = DateTime.now().millis status = "BOOK" nexusUser = extractNexusUser(exchangeId) + bankAccount = exchangeBankAccount } /** This payment is "valid by default" and will be returned * as soon as the exchange will ask for new payments. */ @@ -355,30 +351,26 @@ class Taler(app: Route) { * places it into a further table. Eventually, another routine will perform * all the prepared payments. */ app.post("/ebics/taler/{id}/accounts/{acctid}/refund-invalid-payments") { + val userId = authenticateRequest(call.request.headers["Authorization"]) + val nexusUser = getNexusUser(userId) + val callerBankAccount = expectNonNull(call.parameters["acctid"]) transaction { - val nexusUser = extractNexusUser(call.parameters["id"]) - val acctid = expectAcctidTransaction(call.parameters["acctid"]) - if (!subscriberHasRights(getEbicsSubscriberFromUser(nexusUser), acctid)) { - throw NexusError( - HttpStatusCode.Forbidden, - "The requester can't drive such account (${acctid.id})" - ) - } - val requesterBankAccount = getBankAccountFromNexusUserId(nexusUser.id.value) + val bankAccount = getBankAccount( + userId, + callerBankAccount + ) TalerIncomingPaymentEntity.find { TalerIncomingPayments.refunded eq false and (TalerIncomingPayments.valid eq false) }.forEach { addPreparedPayment( Pain001Data( - creditorName = it.payment.debitorName, - creditorIban = it.payment.debitorIban, + creditorName = it.payment.counterpartName, + creditorIban = it.payment.counterpartIban, creditorBic = it.payment.counterpartBic, sum = calculateRefund(it.payment.amount), subject = "Taler refund", - debitorIban = requesterBankAccount.iban, - debitorBic = requesterBankAccount.bankCode, - debitorName = requesterBankAccount.accountHolder, - currency = it.payment.currency + currency = it.payment.currency, + debitorAccount = callerBankAccount ), nexusUser ) @@ -398,18 +390,22 @@ class Taler(app: Route) { val id = ensureNonNull(call.parameters["id"]) // first find highest ID value of already processed rows. transaction { - val subscriberAccount = getBankAccountFromNexusUserId(id) + val subscriberAccount = getBankAccountsFromNexusUserId(id).first() /** * Search for fresh incoming payments in the raw table, and making pointers - * from the Taler incoming payments table to the found fresh payments. + * from the Taler incoming payments table to the found fresh (and valid!) payments. */ val latestIncomingPaymentId: Long = TalerIncomingPaymentEntity.getLast() RawBankTransactionEntity.find { - /** select payments having the exchange as the credited party */ - RawBankTransactionsTable.creditorIban eq subscriberAccount.iban and + /** Those with exchange bank account involved */ + RawBankTransactionsTable.bankAccount eq subscriberAccount.id.value and + /** Those that are incoming */ + (RawBankTransactionsTable.transactionType eq "CRDT") and + /** Those that are booked */ (RawBankTransactionsTable.status eq "BOOK") and - /** avoid processing old payments from the raw table */ + /** Those that came later than the latest processed payment */ (RawBankTransactionsTable.id.greater(latestIncomingPaymentId)) + }.forEach { if (duplicatePayment(it)) { logger.warn("Incomint payment already seen") @@ -437,8 +433,12 @@ class Taler(app: Route) { */ val latestOutgoingPaymentId = TalerRequestedPaymentEntity.getLast() RawBankTransactionEntity.find { + /** Those that came after the last processed payment */ RawBankTransactionsTable.id greater latestOutgoingPaymentId and - ( RawBankTransactionsTable.debitorIban eq subscriberAccount.iban) + /** Those involving the exchange bank account */ + (RawBankTransactionsTable.bankAccount eq subscriberAccount.id.value) and + /** Those that are outgoing */ + (RawBankTransactionsTable.transactionType eq "DBIT") }.forEach { if (paymentFailed(it)) { logger.error("Bank didn't accept one payment from the exchange") @@ -485,7 +485,7 @@ class Taler(app: Route) { val history = TalerOutgoingHistory() transaction { /** Retrieve all the outgoing payments from the _clean Taler outgoing table_ */ - val subscriberBankAccount = getBankAccountFromNexusUserId(subscriberId) + val subscriberBankAccount = getBankAccountsFromNexusUserId(subscriberId).first() val reqPayments = TalerRequestedPaymentEntity.find { TalerRequestedPayments.rawConfirmed.isNotNull() and startCmpOp }.orderTaler(delta) @@ -539,12 +539,12 @@ class Taler(app: Route) { val nexusUser = extractNexusUser(exchangeId) BankAccountMapEntity.new { bankAccount = newBankAccount - ebicsSubscriber = getEbicsSubscriberFromUser(nexusUser) + ebicsSubscriber = getEbicsTransport(exchangeId) this.nexusUser = nexusUser } } } - val exchangeBankAccount = getBankAccountFromNexusUserId(exchangeId) + val exchangeBankAccount = getBankAccountsFromNexusUserId(exchangeId).first() val orderedPayments = TalerIncomingPaymentEntity.find { TalerIncomingPayments.valid eq true and startCmpOp }.orderTaler(delta) @@ -556,11 +556,15 @@ class Taler(app: Route) { row_id = it.id.value, amount = "${it.payment.currency}:${it.payment.amount}", reserve_pub = it.payment.unstructuredRemittanceInformation, - debit_account = buildPaytoUri( - it.payment.debitorName, it.payment.debitorIban, it.payment.counterpartBic - ), credit_account = buildPaytoUri( - it.payment.creditorName, it.payment.creditorIban, exchangeBankAccount.bankCode + it.payment.bankAccount.accountHolder, + it.payment.bankAccount.iban, + it.payment.bankAccount.bankCode + ), + debit_account = buildPaytoUri( + it.payment.counterpartName, + it.payment.counterpartIban, + it.payment.counterpartBic ) ) ) |