gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated (9187644 -> 16e22ca)


From: gnunet
Subject: [libeufin] branch master updated (9187644 -> 16e22ca)
Date: Wed, 08 Apr 2020 16:54:04 +0200

This is an automated email from the git hooks/post-receive script.

marcello pushed a change to branch master
in repository libeufin.

    from 9187644  define authenticator helper
     new 523a32f  Helper to extract URI parameters.
     new 16e22ca  Completing the incoming history monitor.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt    |  16 +-
 .../src/main/kotlin/tech/libeufin/nexus/Helpers.kt |  21 +++
 nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt  |  19 ++-
 nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt | 163 ++++++++++++++++++---
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    |   8 -
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  |   5 -
 6 files changed, 190 insertions(+), 42 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
index 61922cc..c0f8ec9 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -1,12 +1,10 @@
 package tech.libeufin.nexus
 
+import io.ktor.http.HttpStatusCode
 import org.jetbrains.exposed.dao.*
 import org.jetbrains.exposed.sql.*
 import org.jetbrains.exposed.sql.transactions.TransactionManager
 import org.jetbrains.exposed.sql.transactions.transaction
-import org.joda.time.DateTime
-import tech.libeufin.nexus.EbicsSubscribersTable.entityId
-import tech.libeufin.nexus.EbicsSubscribersTable.primaryKey
 import tech.libeufin.util.IntIdTableWithAmount
 import java.sql.Connection
 
@@ -20,7 +18,17 @@ object TalerIncomingPayments: LongIdTable() {
 }
 
 class TalerIncomingPaymentEntry(id: EntityID<Long>) : LongEntity(id) {
-    companion object : 
LongEntityClass<TalerIncomingPaymentEntry>(TalerIncomingPayments)
+    companion object : 
LongEntityClass<TalerIncomingPaymentEntry>(TalerIncomingPayments) {
+        override fun new(init: TalerIncomingPaymentEntry.() -> Unit): 
TalerIncomingPaymentEntry {
+            val newRow = super.new(init)
+            if (newRow.id.value == Long.MAX_VALUE) {
+                throw NexusError(
+                    HttpStatusCode.InsufficientStorage, "Cannot store rows 
anymore"
+                )
+            }
+            return newRow
+        }
+    }
     var payment by EbicsRawBankTransactionEntry referencedOn 
TalerIncomingPayments.payment
     var valid by TalerIncomingPayments.valid
     var processed by TalerIncomingPayments.processed
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
index 372d414..46d3b8f 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -1,5 +1,6 @@
 package tech.libeufin.nexus
 
+import io.ktor.application.ApplicationCall
 import io.ktor.http.HttpStatusCode
 
 /**
@@ -36,6 +37,26 @@ fun expectIdTransaction(param: String?): 
EbicsSubscriberEntity {
     return EbicsSubscriberEntity.findById(param) ?: throw 
NexusError(HttpStatusCode.NotFound, "Subscriber: $param not found")
 }
 
+fun ApplicationCall.expectUrlParameter(name: String): String {
+    return this.request.queryParameters[name]
+        ?: throw NexusError(HttpStatusCode.BadRequest, "Parameter '$name' not 
provided in URI")
+}
+
+fun expectLong(param: String): Long {
+    return try {
+        param.toLong()
+    } catch (e: Exception) {
+        throw NexusError(HttpStatusCode.BadRequest,"'$param' is not Long")
+    }
+}
+
+fun expectLong(param: String?): Long? {
+    if (param != null) {
+        return expectLong(param)
+    }
+    return null
+}
+
 /* Needs a transaction{} block to be called */
 fun expectAcctidTransaction(param: String?): EbicsAccountInfoEntity {
     if (param == null) {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
index 7daca4d..ec3fe96 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -40,6 +40,7 @@ import io.ktor.routing.post
 import io.ktor.routing.routing
 import io.ktor.server.engine.embeddedServer
 import io.ktor.server.netty.Netty
+import org.jetbrains.exposed.sql.SizedIterable
 import org.jetbrains.exposed.sql.StdOutSqlLogger
 import org.jetbrains.exposed.sql.addLogger
 import org.jetbrains.exposed.sql.and
@@ -138,6 +139,23 @@ fun getSubscriberDetailsFromBankAccount(bankAccountId: 
String): EbicsClientSubsc
     }
 }
 
+/**
+ * Given a subscriber id, returns the _list_ of bank accounts associated to it.
+ * @param id the subscriber id
+ * @return the query set containing the subscriber's bank accounts
+ */
+fun getBankAccountsInfoFromId(id: String): 
SizedIterable<EbicsAccountInfoEntity> {
+    val list = transaction {
+        EbicsAccountInfoEntity.find {
+            EbicsAccountsInfoTable.subscriber eq id
+        }
+    }
+    if (list.empty()) throw NexusError(
+        HttpStatusCode.NotFound, "This subscriber '$id' did never fetch its 
own bank accounts, request HTD first."
+    )
+    return list
+}
+
 fun getSubscriberDetailsFromId(id: String): EbicsClientSubscriberDetails {
     return transaction {
         val subscriber = EbicsSubscriberEntity.findById(id) ?: throw 
NexusError(
@@ -176,7 +194,6 @@ fun getSubscriberDetailsFromId(id: String): 
EbicsClientSubscriberDetails {
  * Needs to be called within a transaction block.
  */
 fun createPain001document(pain001Entity: Pain001Entity): String {
-
     /**
      * Every PAIN.001 document contains at least three IDs:
      *
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
index 9c8ef51..268cb8e 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -3,12 +3,18 @@ package tech.libeufin.nexus
 import io.ktor.application.call
 import io.ktor.http.ContentType
 import io.ktor.http.HttpStatusCode
+import io.ktor.response.respond
 import io.ktor.response.respondText
 import io.ktor.routing.Route
 import io.ktor.routing.get
 import io.ktor.routing.post
-import org.jetbrains.exposed.sql.and
+import org.jetbrains.exposed.dao.EntityID
+import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.SqlExpressionBuilder.lessEq
 import org.jetbrains.exposed.sql.transactions.transaction
+import org.joda.time.DateTime
+import org.joda.time.format.DateTimeFormat
+import org.joda.time.format.DateTimeFormatter
 import tech.libeufin.util.CryptoUtil
 import tech.libeufin.util.base64ToBytes
 import java.lang.Exception
@@ -101,7 +107,6 @@ class Taler(app: Route) {
     private data class TalerOutgoingHistory(
         var outgoing_transactions: MutableList<TalerOutgoingBankTransaction> = 
mutableListOf()
     )
-
     /**
      * Test APIs' data structures.
      */
@@ -116,21 +121,130 @@ class Taler(app: Route) {
         val row_id: Long
     )
 
-    // throws error if password is wrong
-    private fun authenticateRequest(authorization: String?) {
+    private fun SizedIterable<TalerIncomingPaymentEntry>.orderTaler(start: 
Long): List<TalerIncomingPaymentEntry> {
+        return if (start < 0) {
+            this.sortedByDescending { it.id }
+        } else {
+            this.sortedBy { it.id }
+        }
+    }
+
+    /**
+     * Test HTTP basic auth.  Throws error if password is wrong
+     *
+     * @param authorization the Authorization:-header line.
+     * @return subscriber id
+     */
+    private fun authenticateRequest(authorization: String?): String {
         val headerLine = authorization ?: throw NexusError(
             HttpStatusCode.BadRequest, "Authentication:-header line not found"
         )
         logger.debug("Checking for authorization: $headerLine")
-        transaction {
+        val subscriber = transaction {
             val (user, pass) = extractUserAndHashedPassword(headerLine)
             EbicsSubscriberEntity.find {
                 EbicsSubscribersTable.id eq user and 
(EbicsSubscribersTable.password eq SerialBlob(pass))
             }.firstOrNull()
         } ?: throw NexusError(HttpStatusCode.Forbidden, "Wrong password")
+        return subscriber.id.value
+    }
+
+    /**
+     * Implement the Taler wire API transfer method.
+     */
+    private fun transfer(app: Route) {
+
+    }
+
+    private fun getPaytoUri(name: String, iban: String, bic: String): String {
+        return "payto://$iban/$bic?receiver-name=$name"
+    }
+
+    /**
+     * Builds the comparison operator for history entries based on the
+     * sign of 'delta'
+     */
+    private fun getComparisonOperator(delta: Long, start: Long): Op<Boolean> {
+        return if (delta < 0) {
+            Expression.build {
+                TalerIncomingPayments.id less start
+            }
+        } else {
+            Expression.build {
+                TalerIncomingPayments.id greater start
+            }
+        }
     }
 
-    fun testAuth(app: Route) {
+    /**
+     * Helper handling 'start' being optional and its dependence on 'delta'.
+     */
+    private fun handleStartArgument(start: String?, delta: Long): Long {
+        return expectLong(start) ?: if (delta >= 0) {
+            /**
+             * Using -1 as the smallest value, as some DBMS might use 0 and 
some
+             * others might use 1 as the smallest row id.
+             */
+            -1
+        } else {
+            /**
+             * NOTE: the database currently enforces there MAX_VALUE is always
+             * strictly greater than any row's id in the database.  In fact, 
the
+             * database throws exception whenever a new row is going to occupy
+             * the MAX_VALUE with its id.
+             */
+            Long.MAX_VALUE
+        }
+    }
+
+    /**
+     * Respond with ONLY the good transfer made to the exchange.
+     * A 'good' transfer is one whose subject line is a plausible
+     * EdDSA public key encoded in Crockford base32.
+     */
+    private fun historyIncoming(app: Route) {
+        app.get("/taler/history/incoming") {
+            val subscriberId = 
authenticateRequest(call.request.headers["Authorization"])
+            val delta: Long = expectLong(call.expectUrlParameter("delta"))
+            val start: Long = 
handleStartArgument(call.request.queryParameters["start"], delta)
+            val history = TalerIncomingHistory()
+            val cmpOp = getComparisonOperator(delta, start)
+            transaction {
+                val subscriberBankAccount = 
getBankAccountsInfoFromId(subscriberId)
+                TalerIncomingPaymentEntry.find {
+                    TalerIncomingPayments.valid eq true and cmpOp
+                }.orderTaler(start).forEach {
+                    history.incoming_transactions.add(
+                        TalerIncomingBankTransaction(
+                            date = DateTime.parse(it.payment.bookingDate, 
DateTimeFormat.forPattern("YYYY-MM-DD")).millis,
+                            row_id = it.id.value,
+                            amount = 
"${it.payment.currency}:${it.payment.amount}",
+                            reserve_pub = 
it.payment.unstructuredRemittanceInformation,
+                            debit_account = getPaytoUri(
+                                it.payment.debitorName, 
it.payment.debitorIban, it.payment.counterpartBic
+                            ),
+                            credit_account = getPaytoUri(
+                                it.payment.creditorName, 
it.payment.creditorIban, subscriberBankAccount.first().bankCode
+                            )
+                        )
+                    )
+                }
+            }
+            call.respond(history)
+            return@get
+        }
+    }
+
+    /**
+     * Respond with all the transfers that the exchange made to merchants.
+     * It can include also those transfers made to reimburse some invalid
+     * incoming payment.
+     */
+    private fun historyOutgoing(app: Route) {
+
+    }
+
+    private fun testAuth(app: Route) {
         app.get("/taler/test-auth") {
             authenticateRequest(call.request.headers["Authorization"])
             call.respondText("Authenticated!", ContentType.Text.Plain, 
HttpStatusCode.OK)
@@ -138,27 +252,29 @@ class Taler(app: Route) {
         }
     }
 
-    fun digest(app: Route) {
+    private fun digest(app: Route) {
         app.post("/ebics/taler/{id}/digest-incoming-transactions") {
             val id = expectId(call.parameters["id"])
             // first find highest ID value of already processed rows.
             transaction {
-                // avoid re-processing raw payments
-                val latest = 
TalerIncomingPaymentEntry.all().sortedByDescending {
+                /**
+                 * The following query avoids to put a "taler processed" 
flag-column into
+                 * the raw ebics transactions table.  Such table should not 
contain taler-related
+                 * information.
+                 *
+                 * This latestId value points at the latest id in the _raw 
transactions table_
+                 * that was last processed.  On the other hand, the "row_id" 
value that the exchange
+                 * will get along each history element will be the id in the 
_digested entries table_.
+                 */
+                val latestId: Long = 
TalerIncomingPaymentEntry.all().sortedByDescending {
                     it.payment.id
-                }.firstOrNull()
-
-                val payments = if (latest == null) {
-                    EbicsRawBankTransactionEntry.find {
-                        EbicsRawBankTransactionsTable.nexusSubscriber eq id
-                    }
-                } else {
-                    EbicsRawBankTransactionEntry.find {
-                        EbicsRawBankTransactionsTable.id.greater(latest.id) and
-                                (EbicsRawBankTransactionsTable.nexusSubscriber 
eq id)
-                    }
-                }
-                payments.forEach {
+                }.firstOrNull()?.payment?.id?.value ?: -1
+                val subscriberAccount = getBankAccountsInfoFromId(id).first()
+                /* search for fresh transactions having the exchange IBAN in 
the creditor field.  */
+                EbicsRawBankTransactionEntry.find {
+                    EbicsRawBankTransactionsTable.creditorIban eq 
subscriberAccount.iban and
+                            
(EbicsRawBankTransactionsTable.id.greater(latestId))
+                }.forEach {
                     if 
(CryptoUtil.checkValidEddsaPublicKey(it.unstructuredRemittanceInformation)) {
                         TalerIncomingPaymentEntry.new {
                             payment = it
@@ -181,8 +297,7 @@ class Taler(app: Route) {
         }
     }
 
-    fun refund(app: Route) {
-
+    private fun refund(app: Route) {
         
app.post("/ebics/taler/{id}/accounts/{acctid}/refund-invalid-payments") {
             transaction {
                 val subscriber = expectIdTransaction(call.parameters["id"])
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 1916df6..b9d872b 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -221,10 +221,8 @@ object EbicsDownloadTransactionsTable : IdTable<String>() {
     val receiptReceived = bool("receiptReceived")
 }
 
-
 class EbicsDownloadTransactionEntity(id: EntityID<String>) : 
Entity<String>(id) {
     companion object : EntityClass<String, 
EbicsDownloadTransactionEntity>(EbicsDownloadTransactionsTable)
-
     var orderType by EbicsDownloadTransactionsTable.orderType
     var host by EbicsHostEntity referencedOn 
EbicsDownloadTransactionsTable.host
     var subscriber by EbicsSubscriberEntity referencedOn 
EbicsDownloadTransactionsTable.subscriber
@@ -235,7 +233,6 @@ class EbicsDownloadTransactionEntity(id: EntityID<String>) 
: Entity<String>(id)
     var receiptReceived by EbicsDownloadTransactionsTable.receiptReceived
 }
 
-
 object EbicsUploadTransactionsTable : IdTable<String>() {
     override val id = text("transactionID").entityId()
     val orderType = text("orderType")
@@ -247,7 +244,6 @@ object EbicsUploadTransactionsTable : IdTable<String>() {
     val transactionKeyEnc = blob("transactionKeyEnc")
 }
 
-
 class EbicsUploadTransactionEntity(id: EntityID<String>) : Entity<String>(id) {
     companion object : EntityClass<String, 
EbicsUploadTransactionEntity>(EbicsUploadTransactionsTable)
 
@@ -260,7 +256,6 @@ class EbicsUploadTransactionEntity(id: EntityID<String>) : 
Entity<String>(id) {
     var transactionKeyEnc by EbicsUploadTransactionsTable.transactionKeyEnc
 }
 
-
 object EbicsOrderSignaturesTable : IntIdTable() {
     val orderID = text("orderID")
     val orderType = text("orderType")
@@ -270,10 +265,8 @@ object EbicsOrderSignaturesTable : IntIdTable() {
     val signatureValue = blob("signatureValue")
 }
 
-
 class EbicsOrderSignatureEntity(id: EntityID<Int>) : IntEntity(id) {
     companion object : 
IntEntityClass<EbicsOrderSignatureEntity>(EbicsOrderSignaturesTable)
-
     var orderID by EbicsOrderSignaturesTable.orderID
     var orderType by EbicsOrderSignaturesTable.orderType
     var partnerID by EbicsOrderSignaturesTable.partnerID
@@ -291,7 +284,6 @@ object EbicsUploadTransactionChunksTable : 
IdTable<String>() {
 
 class EbicsUploadTransactionChunkEntity(id : EntityID<String>): 
Entity<String>(id) {
     companion object : EntityClass<String, 
EbicsUploadTransactionChunkEntity>(EbicsUploadTransactionChunksTable)
-
     var chunkIndex by EbicsUploadTransactionChunksTable.chunkIndex
     var chunkContent by EbicsUploadTransactionChunksTable.chunkContent
 }
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index ea85bf5..2af9c6a 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -225,7 +225,6 @@ fun main() {
             }
         }
         routing {
-
             post("/{id}/history") {
                 val req = call.receive<CustomerHistoryRequest>()
                 val customer = findCustomer(call.parameters["id"])
@@ -252,7 +251,6 @@ fun main() {
                 call.respond(ret)
                 return@post
             }
-
             get("/{id}/balance") {
                 val customer = findCustomer(call.parameters["id"])
                 val balance = calculateBalance(customer.id.value, null, null)
@@ -264,7 +262,6 @@ fun main() {
                 )
                 return@get
             }
-
             get("/admin/subscribers") {
                 var ret = AdminGetSubscribers()
                 transaction {
@@ -277,7 +274,6 @@ fun main() {
                 call.respond(ret)
                 return@get
             }
-
             post("/admin/add/subscriber") {
                 val body = call.receive<AdminAddSubscriberRequest>()
 
@@ -332,7 +328,6 @@ fun main() {
                     HttpStatusCode.OK
                 )
                 return@post
-
             }
             get("/ebics/hosts/{id}") {
                 val resp = transaction {

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]