gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated (1aa93620 -> 5463134c)


From: gnunet
Subject: [libeufin] branch master updated (1aa93620 -> 5463134c)
Date: Wed, 17 Jan 2024 14:10:03 +0100

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

antoine pushed a change to branch master
in repository libeufin.

    from 1aa93620 better i18n for es
     new 5efc69c4 receiver-name in cashout payto
     new 5463134c Fix ebics-fetch parse without import feature

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:
 .../main/kotlin/tech/libeufin/bank/TalerCommon.kt  |   3 +
 .../kotlin/tech/libeufin/bank/db/AccountDAO.kt     | 119 +++++++++++----------
 bank/src/test/kotlin/CoreBankApiTest.kt            |  61 ++++++++++-
 .../main/kotlin/tech/libeufin/nexus/EbicsFetch.kt  |   8 +-
 4 files changed, 125 insertions(+), 66 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
index e37a1661..5f8830cb 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
@@ -454,14 +454,17 @@ class IbanPayTo: PaytoUri {
         receiverName = params["receiver-name"]
     }
 
+    /** Canonical IBAN payto with receiver-name parameter if present */
     fun maybeFull(): String {
         return canonical + if (receiverName != null) ("?receiver-name=" + 
receiverName.encodeURLParameter()) else ""
     }
 
+    /** Canonical IBAN payto with receiver-name parameter, fail if absent */
     fun expectFull(): String {
         return canonical + "?receiver-name=" + 
receiverName!!.encodeURLParameter()
     }
 
+    /** Canonical IBAN payto with receiver-name parameter set to [defaultName] 
if absent */
     fun fullOptName(defaultName: String): String {
         return canonical + "?receiver-name=" + (receiverName ?: 
defaultName).encodeURLParameter()
     }
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
index ac9b6a96..e9e30c52 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
@@ -240,83 +240,88 @@ class AccountDAO(private val db: Database) {
         val checkCashout = !isAdmin && !allowEditCashout && 
cashoutPayto.isSome()
         val checkDebtLimit = !isAdmin && debtLimit != null
 
-        // Get user ID and check reconfig rights TODO checkout with name
-        val (customerId, currChannel, currInfo) = conn.prepareStatement("""
-            SELECT
-                customer_id
-                ,(${ if (checkName) "name != ?" else "false" }) as name_change
-                ,(${ if (checkCashout) "cashout_payto IS DISTINCT FROM ?" else 
"false" }) as cashout_change
-                ,(${ if (checkDebtLimit) "max_debt != (?, ?)::taler_amount" 
else "false" }) as debt_limit_change
-                ,(${ when (tan_channel.get()) {
-                    null -> "false"
-                    TanChannel.sms -> if (phone.get() != null) "false" else 
"phone IS NULL"
-                    TanChannel.email -> if (email.get() != null) "false" else 
"email IS NULL"
-                }}) as missing_tan_info
-                ,tan_channel, phone, email
+        data class CurrentAccount(
+            val id: Long,
+            val channel: TanChannel?,
+            val email: String?,
+            val phone: String?,
+            val name: String,
+            val cashoutPayTo: String?,
+            val debtLimit: TalerAmount,
+        )
+
+        // Get user ID and current data
+        val curr = conn.prepareStatement("""
+            SELECT 
+                customer_id, tan_channel, phone, email, name, cashout_payto
+                ,(max_debt).val AS max_debt_val
+                ,(max_debt).frac AS max_debt_frac
             FROM customers
                 JOIN bank_accounts 
                 ON customer_id=owning_customer_id
             WHERE login=?
         """).run {
-            var idx = 1;
-            if (checkName) {
-                setString(idx, name); idx++
-            }
-            if (checkCashout) {
-                setString(idx, cashoutPayto.get()?.maybeFull()); idx++ // TODO 
cashout with name
-            }
-            if (checkDebtLimit) {
-                setLong(idx, debtLimit!!.value); idx++
-                setInt(idx, debtLimit.frac); idx++
-            }
-            setString(idx, login)
-            executeQuery().use {
-                when {
-                    !it.next() -> return@transaction 
AccountPatchResult.UnknownAccount
-                    it.getBoolean("name_change") -> return@transaction 
AccountPatchResult.NonAdminName
-                    it.getBoolean("cashout_change") -> return@transaction 
AccountPatchResult.NonAdminCashout
-                    it.getBoolean("debt_limit_change") -> return@transaction 
AccountPatchResult.NonAdminDebtLimit
-                    it.getBoolean("missing_tan_info") -> return@transaction 
AccountPatchResult.MissingTanInfo
-                    else -> {
-                        val currChannel = it.getString("tan_channel")?.run { 
TanChannel.valueOf(this) }
-                        Triple(
-                            it.getLong("customer_id"),
-                            currChannel,
-                            when (tan_channel.get() ?: currChannel) {
-                                TanChannel.sms -> it.getString("phone")
-                                TanChannel.email -> it.getString("email")
-                                null -> null
-                            }
-                        )
-                    }
-                }
-            }
+            setString(1, login)
+            oneOrNull {
+                CurrentAccount(
+                    id = it.getLong("customer_id"),
+                    channel = it.getString("tan_channel")?.run { 
TanChannel.valueOf(this) },
+                    phone = it.getString("phone"),
+                    email = it.getString("email"),
+                    name = it.getString("name"),
+                    cashoutPayTo = it.getString("cashout_payto"),
+                    debtLimit = it.getAmount("max_debt", db.bankCurrency),
+                )
+            } ?: return@transaction AccountPatchResult.UnknownAccount
         }
- 
-        val newChannel = tan_channel.get();
-        val newInfo = when (newChannel ?: currChannel) {
+
+        // Patched TAN channel
+        val patchChannel = tan_channel.get()
+        // TAN channel after the PATCH
+        val newChannel = patchChannel ?: curr.channel
+        // Patched TAN info
+        val patchInfo = when (newChannel) {
             TanChannel.sms -> phone.get()
             TanChannel.email -> email.get()
             null -> null
         }
+        // TAN info after the PATCH
+        val newInfo = patchInfo ?: when (newChannel) {
+            TanChannel.sms -> curr.phone
+            TanChannel.email -> curr.email
+            null -> null
+        }
+        // Cashout payto with a receiver-name using if receiver-name is 
missing the new named if present or the current one 
+        val cashoutPaytoNamed = cashoutPayto.get()?.fullOptName(name ?: 
curr.name)
+
+        // Check reconfig rights
+        if (checkName && name != curr.name) 
+            return@transaction AccountPatchResult.NonAdminName
+        if (checkCashout && cashoutPaytoNamed != curr.cashoutPayTo) 
+            return@transaction AccountPatchResult.NonAdminCashout
+        if (checkDebtLimit && debtLimit != curr.debtLimit)
+            return@transaction AccountPatchResult.NonAdminDebtLimit
+        if (patchChannel != null && newInfo == null)
+            return@transaction AccountPatchResult.MissingTanInfo
+
 
         // Tan channel verification
         if (!isAdmin) {
             // Check performed 2fa check
-            if (currChannel != null && !is2fa) {
+            if (curr.channel != null && !is2fa) {
                 // Perform challenge with current settings
                 return@transaction AccountPatchResult.TanRequired(channel = 
null, info = null)
             }
             // If channel or info changed and the 2fa challenge is performed 
with old settings perform a new challenge with new settings
-            if ((newChannel != null && newChannel != faChannel) || (newInfo != 
null && newInfo != faInfo)) {
-                return@transaction AccountPatchResult.TanRequired(channel = 
newChannel ?: currChannel, info = newInfo ?: currInfo)
+            if ((patchChannel != null && patchChannel != faChannel) || 
(patchInfo != null && patchInfo != faInfo)) {
+                return@transaction AccountPatchResult.TanRequired(channel = 
newChannel, info = newInfo)
             }
         }
 
         // Invalidate current challenges
-        if (newChannel != null || newInfo != null) {
+        if (patchChannel != null || patchInfo != null) {
             val stmt = conn.prepareStatement("UPDATE tan_challenges SET 
expiration_date=0 WHERE customer=?")
-            stmt.setLong(1, customerId)
+            stmt.setLong(1, curr.id)
             stmt.execute()
         }
 
@@ -331,7 +336,7 @@ class AccountDAO(private val db: Database) {
             sequence {
                 isPublic?.let { yield(it) }
                 debtLimit?.let { yield(it.value); yield(it.frac) }
-                yield(customerId)
+                yield(curr.id)
             }
         )
 
@@ -347,12 +352,12 @@ class AccountDAO(private val db: Database) {
             },
             "WHERE customer_id = ?",
             sequence {
-                cashoutPayto.some { yield(it?.canonical) }
+                cashoutPayto.some { yield(cashoutPaytoNamed) }
                 phone.some { yield(it) }
                 email.some { yield(it) }
                 tan_channel.some { yield(it?.name) }
                 name?.let { yield(it) }
-                yield(customerId)
+                yield(curr.id)
             }
         )
 
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt 
b/bank/src/test/kotlin/CoreBankApiTest.kt
index 5a031b87..c9ecfa03 100644
--- a/bank/src/test/kotlin/CoreBankApiTest.kt
+++ b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -195,7 +195,7 @@ class CoreBankAccountsApiTest {
         }
 
         // Check given payto
-        val ibanPayto = genIbanPaytoUri()
+        val ibanPayto = IbanPayTo(genIbanPaytoUri())
         val req = obj {
             "username" to "foo"
             "password" to "password"
@@ -208,7 +208,7 @@ class CoreBankAccountsApiTest {
         client.post("/accounts") {
             json(req)
         }.assertOkJson<RegisterAccountResponse> {
-            assertEquals(ibanPayto, it.internal_payto_uri)
+            assertEquals(ibanPayto.canonical, it.internal_payto_uri)
         }
         // Testing idempotency
         client.post("/accounts") {
@@ -298,6 +298,31 @@ class CoreBankAccountsApiTest {
         client.get("/accounts/bar") {
             pwAuth("admin")
         }.assertNotFound(TalerErrorCode.BANK_UNKNOWN_ACCOUNT)
+
+        // Check cashout payto receiver name logic
+        client.post("/accounts") {
+            json {
+                "username" to "cashout_guess"
+                "password" to "cashout_guess-password"
+                "name" to "Mr Guess My Name"
+                "cashout_payto_uri" to ibanPayto
+            }
+        }.assertOk()
+        client.getA("/accounts/cashout_guess").assertOkJson<AccountData> {
+            assertEquals(ibanPayto.fullOptName("Mr Guess My Name"), 
it.cashout_payto_uri)
+        }
+        val full = ibanPayto.fullOptName("Santa Claus")
+        client.post("/accounts") {
+            json {
+                "username" to "cashout_keep"
+                "password" to "cashout_keep-password"
+                "name" to "Mr Keep My Name"
+                "cashout_payto_uri" to full
+            }
+        }.assertOk()
+        client.getA("/accounts/cashout_keep").assertOkJson<AccountData> {
+            assertEquals(full, it.cashout_payto_uri)
+        }
     }
 
     // Test account created with bonus
@@ -465,7 +490,7 @@ class CoreBankAccountsApiTest {
         // Successful attempt now
         val cashout = IbanPayTo(genIbanPaytoUri())
         val req = obj {
-            "cashout_payto_uri" to cashout.canonical
+            "cashout_payto_uri" to cashout
             "name" to "Roger"
             "is_public" to true
             "contact_data" to obj {
@@ -496,7 +521,7 @@ class CoreBankAccountsApiTest {
         // Check patch
         client.getA("/accounts/merchant").assertOkJson<AccountData> { obj ->
             assertEquals("Roger", obj.name)
-            assertEquals(cashout.canonical, obj.cashout_payto_uri)
+            assertEquals(cashout.fullOptName(obj.name), obj.cashout_payto_uri)
             assertEquals("+99", obj.contact_data?.phone?.get())
             assertEquals("foo@example.com", obj.contact_data?.email?.get())
             assertEquals(TalerAmount("KUDOS:100"), obj.debit_threshold)
@@ -510,7 +535,7 @@ class CoreBankAccountsApiTest {
         }.assertNoContent()
         client.getA("/accounts/merchant").assertOkJson<AccountData> { obj ->
             assertEquals("Roger", obj.name)
-            assertEquals(cashout.canonical, obj.cashout_payto_uri)
+            assertEquals(cashout.fullOptName(obj.name), obj.cashout_payto_uri)
             assertEquals("+99", obj.contact_data?.phone?.get())
             assertEquals("foo@example.com", obj.contact_data?.email?.get())
             assertEquals(TalerAmount("KUDOS:100"), obj.debit_threshold)
@@ -525,6 +550,32 @@ class CoreBankAccountsApiTest {
             }
         }.assertConflict(TalerErrorCode.END)
 
+        // Check cashout payto receiver name logic
+        client.post("/accounts") {
+            json {
+                "username" to "cashout"
+                "password" to "cashout-password"
+                "name" to "Mr Cashout Cashout"
+            }
+        }.assertOk()
+        for ((cashout, name, expect) in listOf(
+            Triple(cashout.canonical, null, cashout.fullOptName("Mr Cashout 
Cashout")),
+            Triple(cashout.canonical, "New name", cashout.fullOptName("New 
name")),
+            Triple(cashout.fullOptName("Full name"), null, 
cashout.fullOptName("Full name")),
+            Triple(cashout.fullOptName("Full second name"), "Another name", 
cashout.fullOptName("Full second name"))
+        )) {
+            client.patch("/accounts/cashout") {
+                pwAuth("admin")
+                json {
+                    "cashout_payto_uri" to cashout
+                    if (name != null) "name" to name
+                }
+            }.assertNoContent()
+            client.getA("/accounts/cashout").assertOkJson<AccountData> { obj ->
+                assertEquals(expect, obj.cashout_payto_uri)
+            }
+        }
+
         // Check 2FA
         fillTanInfo("merchant")
         client.patchA("/accounts/merchant") {
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
index d4feaf98..05952d21 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsFetch.kt
@@ -340,7 +340,7 @@ fun firstLessThanSecond(
 }
 
 private fun ingestDocument(
-    db: Database,
+    db: Database?,
     currency: String,
     content: ByteArray,
     whichDocument: SupportedDocument
@@ -364,10 +364,10 @@ private fun ingestDocument(
 
                 runBlocking {
                     incomingPayments.forEach {
-                        ingestIncomingPayment(db, it)
+                        if (db != null) ingestIncomingPayment(db, it) else 
logger.debug("$it")
                     }
                     outgoingPayments.forEach {
-                        ingestOutgoingPayment(db, it)
+                        if (db != null) ingestOutgoingPayment(db, it) else 
logger.debug("$it")
                     }
                 }
             } catch (e: Exception) {
@@ -513,7 +513,7 @@ class EbicsFetch: CliktCommand("Fetches bank records.  
Defaults to camt.054 noti
             if (parse || import) {
                 logger.debug("Reading from STDIN, running in debug mode.  Not 
involving the database.")
                 val stdin = 
generateSequence(::readLine).joinToString("\n").toByteArray()
-                ingestDocument(db, cfg.currency, stdin, whichDoc) // TODO no db
+                ingestDocument(if (import) db else null, cfg.currency, stdin, 
whichDoc)
                 return@cliCmd
             }
 

-- 
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]