gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated: Checking big amounts.


From: gnunet
Subject: [libeufin] branch master updated: Checking big amounts.
Date: Fri, 29 Sep 2023 14:26:08 +0200

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

ms pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new 642d36b0 Checking big amounts.
642d36b0 is described below

commit 642d36b0b738311dd0e12212f664a237bd64138c
Author: MS <ms@taler.net>
AuthorDate: Fri Sep 29 14:23:17 2023 +0200

    Checking big amounts.
    
    Checking that debt limits are honored and that gigantic
    amounts fail soon at parsing time.
---
 .../tech/libeufin/bank/CorebankApiHandlers.kt      | 28 +++++++++-----
 .../src/main/kotlin/tech/libeufin/bank/Database.kt |  7 ++--
 bank/src/test/kotlin/LibeuFinApiTest.kt            | 44 ++++++++++++++++++++++
 bank/src/test/kotlin/TalerApiTest.kt               | 25 +++++++-----
 database-versioning/procedures.sql                 |  1 +
 5 files changed, 82 insertions(+), 23 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
index fc3ca211..b11f29ac 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
@@ -240,6 +240,8 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
         val accountName = call.expectUriComponent("USERNAME")
         if (c.login != accountName) throw unauthorized("User ${c.login} not 
allowed to withdraw for account '${accountName}'")
         val req = call.receive<BankAccountCreateWithdrawalRequest>() // 
Checking that the user has enough funds.
+        if(req.amount.currency != ctx.currency)
+            throw badRequest("Wrong currency: ${req.amount.currency}")
         val b = db.bankAccountGetFromOwnerId(c.expectRowId())
             ?: throw internalServerError("Customer '${c.login}' lacks bank 
account.")
         if (!isBalanceEnough(
@@ -375,26 +377,34 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
 
     // Creates a bank transaction.
     post("/accounts/{USERNAME}/transactions") {
-        val c = call.authenticateBankRequest(db, TokenScope.readwrite) ?: 
throw unauthorized()
+        val c: Customer = call.authenticateBankRequest(db, 
TokenScope.readwrite) ?: throw unauthorized()
         val resourceName = call.expectUriComponent("USERNAME") // admin has no 
rights here.
         if ((c.login != resourceName) && (call.getAuthToken() == null)) throw 
forbidden()
         val txData = call.receive<BankAccountTransactionCreate>()
         val payto = parsePayto(txData.payto_uri) ?: throw badRequest("Invalid 
creditor Payto")
         val paytoWithoutParams = stripIbanPayto(txData.payto_uri)
         val subject = payto.message ?: throw badRequest("Wire transfer lacks 
subject")
-        val debtorId = c.dbRowId
-            ?: throw internalServerError("Debtor database ID not found") // 
This performs already a SELECT on the bank account, like the wire transfer will 
do as well later!
+        val debtorBankAccount = db.bankAccountGetFromOwnerId(c.expectRowId())
+            ?: throw internalServerError("Debtor bank account not found")
+        if (txData.amount.currency != ctx.currency) throw badRequest(
+            "Wrong currency: ${txData.amount.currency}",
+            talerErrorCode = TalerErrorCode.TALER_EC_GENERIC_CURRENCY_MISMATCH
+        )
+        if (!isBalanceEnough(
+            balance = debtorBankAccount.expectBalance(),
+            due = txData.amount,
+            hasBalanceDebt = debtorBankAccount.hasDebt,
+            maxDebt = debtorBankAccount.maxDebt
+        ))
+            throw conflict(hint = "Insufficient balance.", talerEc = 
TalerErrorCode.TALER_EC_BANK_UNALLOWED_DEBIT)
         logger.info("creditor payto: $paytoWithoutParams")
-        val creditorCustomerData = 
db.bankAccountGetFromInternalPayto(paytoWithoutParams) ?: throw notFound(
+        val creditorBankAccount = 
db.bankAccountGetFromInternalPayto(paytoWithoutParams) ?: throw notFound(
             "Creditor account not found",
             TalerErrorCode.TALER_EC_BANK_UNKNOWN_ACCOUNT
         )
-        if (txData.amount.currency != ctx.currency) throw badRequest(
-            "Wrong currency: ${txData.amount.currency}", talerErrorCode = 
TalerErrorCode.TALER_EC_GENERIC_CURRENCY_MISMATCH
-        )
         val dbInstructions = BankInternalTransaction(
-            debtorAccountId = debtorId,
-            creditorAccountId = creditorCustomerData.owningCustomerId,
+            debtorAccountId = debtorBankAccount.expectRowId(),
+            creditorAccountId = creditorBankAccount.expectRowId(),
             subject = subject,
             amount = txData.amount,
             transactionDate = Instant.now()
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
index 07ae1bd3..a402462c 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
@@ -21,6 +21,7 @@
 package tech.libeufin.bank
 
 import org.postgresql.jdbc.PgConnection
+import org.postgresql.util.PSQLException
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import tech.libeufin.util.getJdbcConnectionFromPg
@@ -176,10 +177,8 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
             stmt.execute()
         } catch (e: SQLException) {
             logger.error(e.message)
-            // NOTE: it seems that _every_ error gets the 0 code.
-            if (e.errorCode == 0) return false
-            // rethrowing, not to hide other types of errors.
-            throw e
+            if (e.sqlState == "23505") return false // unique_violation
+            throw e // rethrowing, not to hide other types of errors.
         }
         return true
     }
diff --git a/bank/src/test/kotlin/LibeuFinApiTest.kt 
b/bank/src/test/kotlin/LibeuFinApiTest.kt
index a1d29720..eb44f06b 100644
--- a/bank/src/test/kotlin/LibeuFinApiTest.kt
+++ b/bank/src/test/kotlin/LibeuFinApiTest.kt
@@ -130,6 +130,50 @@ class LibeuFinApiTest {
             }
             val obj: BankAccountTransactionInfo = 
Json.decodeFromString(r.bodyAsText())
             assert(obj.subject == "payout")
+            // Testing the wrong currency.
+            val wrongCurrencyResp = client.post("/accounts/foo/transactions") {
+                expectSuccess = false
+                basicAuth("foo", "pw")
+                contentType(ContentType.Application.Json)
+                // expectSuccess = true
+                setBody(
+                    """{
+                    "payto_uri": "payto://iban/AC${barId}?message=payout", 
+                    "amount": "EUR:3.3"
+                }
+                """.trimIndent()
+                )
+            }
+            assert(wrongCurrencyResp.status == HttpStatusCode.BadRequest)
+            // Surpassing the debt limit.
+            val unallowedDebtResp = client.post("/accounts/foo/transactions") {
+                expectSuccess = false
+                basicAuth("foo", "pw")
+                contentType(ContentType.Application.Json)
+                // expectSuccess = true
+                setBody(
+                    """{
+                    "payto_uri": "payto://iban/AC${barId}?message=payout", 
+                    "amount": "KUDOS:555"
+                }
+                """.trimIndent()
+                )
+            }
+            assert(unallowedDebtResp.status == HttpStatusCode.Conflict)
+            val bigAmount = client.post("/accounts/foo/transactions") {
+                expectSuccess = false
+                basicAuth("foo", "pw")
+                contentType(ContentType.Application.Json)
+                // expectSuccess = true
+                setBody(
+                    """{
+                    "payto_uri": "payto://iban/AC${barId}?message=payout", 
+                    "amount": "KUDOS:${"5".repeat(200)}"
+                }
+                """.trimIndent()
+                )
+            }
+            assert(bigAmount.status == HttpStatusCode.BadRequest)
         }
     }
 
diff --git a/bank/src/test/kotlin/TalerApiTest.kt 
b/bank/src/test/kotlin/TalerApiTest.kt
index a78a7a1a..37270b9d 100644
--- a/bank/src/test/kotlin/TalerApiTest.kt
+++ b/bank/src/test/kotlin/TalerApiTest.kt
@@ -53,11 +53,6 @@ class TalerApiTest {
         assert(db.bankAccountCreate(bankAccountFoo) != null)
         assert(db.customerCreate(customerBar) != null)
         assert(db.bankAccountCreate(bankAccountBar) != null)
-        // Give the exchange reasonable debt allowance:
-        assert(db.bankAccountSetMaxDebt(
-            1L,
-            TalerAmount(1000, 0, "KUDOS")
-        ))
         // Do POST /transfer.
         testApplication {
             application {
@@ -68,17 +63,29 @@ class TalerApiTest {
                       "request_uid": "entropic 0",
                       "wtid": "entropic 1",
                       "exchange_base_url": "http://exchange.example.com/";,
-                      "amount": "KUDOS:33",
+                      "amount": "KUDOS:55",
                       "credit_account": "BAR-IBAN-ABC"
                     }
                 """.trimIndent()
+            // Checking exchange debt constraint.
+            val resp = 
client.post("/accounts/foo/taler-wire-gateway/transfer") {
+                basicAuth("foo", "pw")
+                contentType(ContentType.Application.Json)
+                expectSuccess = false
+                setBody(req)
+            }
+            assert(resp.status == HttpStatusCode.Conflict)
+            // Giving debt allowance and checking the OK case.
+            assert(db.bankAccountSetMaxDebt(
+                1L,
+                TalerAmount(1000, 0, "KUDOS")
+            ))
             client.post("/accounts/foo/taler-wire-gateway/transfer") {
                 basicAuth("foo", "pw")
                 contentType(ContentType.Application.Json)
                 expectSuccess = true
                 setBody(req)
             }
-            // println(resp.bodyAsText())
             // check idempotency
             client.post("/accounts/foo/taler-wire-gateway/transfer") {
                 basicAuth("foo", "pw")
@@ -86,7 +93,6 @@ class TalerApiTest {
                 expectSuccess = true
                 setBody(req)
             }
-            // println(idemResp.bodyAsText())
             // Trigger conflict due to reused request_uid
             val r = client.post("/accounts/foo/taler-wire-gateway/transfer") {
                 basicAuth("foo", "pw")
@@ -103,8 +109,7 @@ class TalerApiTest {
                 """.trimIndent())
             }
             assert(r.status == HttpStatusCode.Conflict)
-            /* Triggering currency mismatch.  This mainly tests
-             * the TalerAmount "@Contextual" parser.  */
+            // Triggering currency mismatch
             val currencyMismatchResp = 
client.post("/accounts/foo/taler-wire-gateway/transfer") {
                 basicAuth("foo", "pw")
                 contentType(ContentType.Application.Json)
diff --git a/database-versioning/procedures.sql 
b/database-versioning/procedures.sql
index 25cc8df8..652219ba 100644
--- a/database-versioning/procedures.sql
+++ b/database-versioning/procedures.sql
@@ -146,6 +146,7 @@ SELECT
 IF (maybe_balance_insufficient)
 THEN
   out_exchange_balance_insufficient=TRUE;
+  RETURN;
 END IF;
 out_exchange_balance_insufficient=FALSE;
 INSERT

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

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