gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated: Access API: GETting withdrawal informa


From: gnunet
Subject: [libeufin] branch master updated: Access API: GETting withdrawal information.
Date: Thu, 21 Oct 2021 09:14:19 +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 4474752  Access API: GETting withdrawal information.
4474752 is described below

commit 4474752e53c0ffdec32150282bd85e88261eb020
Author: ms <ms@taler.net>
AuthorDate: Thu Oct 21 09:13:51 2021 +0200

    Access API: GETting withdrawal information.
---
 .../src/main/kotlin/tech/libeufin/sandbox/DB.kt    |  4 +-
 .../main/kotlin/tech/libeufin/sandbox/Helpers.kt   |  9 +++
 .../src/main/kotlin/tech/libeufin/sandbox/JSON.kt  |  8 +++
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 68 ++++++++++++++++------
 util/src/main/kotlin/HTTP.kt                       |  8 +++
 util/src/main/kotlin/amounts.kt                    |  7 +++
 util/src/main/kotlin/strings.kt                    |  7 ---
 7 files changed, 83 insertions(+), 28 deletions(-)

diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
index 4aaac47..47b1e34 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -430,7 +430,7 @@ object TalerWithdrawalsTable : LongIdTable() {
      * gets completed _on the bank's side_.  This does never guarantees that
      * the payment arrived at the exchange's bank yet.
      */
-    val transferDone = bool("transferDone").default(false)
+    val confirmationDone = bool("confirmationDone").default(false)
     val reservePub = text("reservePub").nullable()
     val selectedExchangePayto = text("selectedExchangePayto").nullable()
     val walletBankAccount = reference("walletBankAccount", BankAccountsTable)
@@ -439,7 +439,7 @@ class TalerWithdrawalEntity(id: EntityID<Long>) : 
LongEntity(id) {
     companion object : 
LongEntityClass<TalerWithdrawalEntity>(TalerWithdrawalsTable)
     var wopid by TalerWithdrawalsTable.wopid
     var selectionDone by TalerWithdrawalsTable.selectionDone
-    var transferDone by TalerWithdrawalsTable.transferDone
+    var confirmationDone by TalerWithdrawalsTable.confirmationDone
     var reservePub by TalerWithdrawalsTable.reservePub
     var selectedExchangePayto by TalerWithdrawalsTable.selectedExchangePayto
     var amount by TalerWithdrawalsTable.amount
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
index 75c876e..4aef3c2 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Helpers.kt
@@ -151,6 +151,15 @@ fun wireTransfer(
     return transactionRef
 }
 
+fun getWithdrawalOperation(opId: String): TalerWithdrawalEntity {
+    return transaction {
+        TalerWithdrawalEntity.find {
+            TalerWithdrawalsTable.wopid eq java.util.UUID.fromString(opId)
+        }.firstOrNull() ?: throw SandboxError(
+            HttpStatusCode.NotFound, "Withdrawal operation $opId not found."
+        )
+    }
+}
 
 fun getBankAccountFromPayto(paytoUri: String): BankAccountEntity {
     val paytoParse = parsePayto(paytoUri)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
index 3447c4b..86758b2 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/JSON.kt
@@ -22,6 +22,14 @@ package tech.libeufin.sandbox
 import tech.libeufin.util.PaymentInfo
 import tech.libeufin.util.RawPayment
 
+data class WithdrawalRequest(
+    /**
+     * Note: the currency is redundant, because at each point during
+     * the execution the Demobank should have a handle of the currency.
+     */
+    val amount: String // $CURRENCY:X.Y
+)
+
 data class Demobank(
     val currency: String,
     val name: String,
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 487c71f..a5dee37 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -63,7 +63,6 @@ import io.ktor.server.engine.*
 import io.ktor.server.netty.*
 import io.ktor.util.*
 import io.ktor.util.date.*
-import io.ktor.util.pipeline.*
 import kotlinx.coroutines.newSingleThreadContext
 import org.jetbrains.exposed.sql.*
 import org.jetbrains.exposed.sql.statements.api.ExposedBlob
@@ -72,6 +71,7 @@ import org.jetbrains.exposed.sql.transactions.transaction
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import org.w3c.dom.Document
+import parseAmount
 import startServer
 import tech.libeufin.util.*
 import validatePlainAmount
@@ -960,7 +960,7 @@ val sandboxApp: Application.() -> Unit = {
                             HttpStatusCode.NotFound, "Withdrawal operation 
$wopid not found."
                         )
                         if (wo.selectionDone) {
-                            if (wo.transferDone) {
+                            if (wo.confirmationDone) {
                                 logger.info("Wallet performs again this 
operation that was paid out earlier: idempotent")
                                 return@newSuspendedTransaction
                             }
@@ -977,7 +977,7 @@ val sandboxApp: Application.() -> Unit = {
                         wo.reservePub = body.reserve_pub
                         wo.selectedExchangePayto = body.selected_exchange
                         wo.selectionDone = true
-                        wo.transferDone
+                        wo.confirmationDone
                     }
                     call.respond(object {
                         val transfer_done = transferDone
@@ -997,7 +997,7 @@ val sandboxApp: Application.() -> Unit = {
                     val demobank = ensureDemobank(call)
                     val ret = TalerWithdrawalStatus(
                         selection_done = wo.selectionDone,
-                        transfer_done = wo.transferDone,
+                        transfer_done = wo.confirmationDone,
                         amount = wo.amount,
                         suggested_exchange = demobank.suggestedExchange
                     )
@@ -1007,13 +1007,31 @@ val sandboxApp: Application.() -> Unit = {
             }
             // Talk to Web UI.
             route("/access-api") {
+                // Information about one withdrawal.
+                get("/accounts/{account_name}/withdrawals/{withdrawal_id}") {
+                    val op = 
getWithdrawalOperation(call.getUriComponent("withdrawal_id"))
+                    val demobank = ensureDemobank(call)
+                    if (!op.selectionDone && op.reservePub != null) throw 
internalServerError(
+                        "Unselected withdrawal has a reserve public key",
+                        LibeufinErrorCode.LIBEUFIN_EC_INCONSISTENT_STATE
+                    )
+                    call.respond(object {
+                        val amount = "${demobank.currency}:${op.amount}"
+                        val aborted = op.aborted
+                        val confirmation_done = op.confirmationDone
+                        val selection_done = op.selectionDone
+                        val selected_reserve_pub = op.reservePub
+                        val selected_exchange_account = 
op.selectedExchangePayto
+                    })
+                    return@get
+                }
                 // Create a new withdrawal operation.
                 post("/accounts/{account_name}/withdrawals") {
-                    val username = call.request.basicAuth()
-                    if (username == null) throw badRequest(
-                        "Taler withdrawal tried with authentication disabled. 
" +
-                                "That is impossible, because no bank account 
can get this operation debited."
-                    )
+                    var username = call.request.basicAuth()
+                    if (username == null && (!WITH_AUTH)) {
+                        logger.info("Authentication is disabled to facilitate 
tests, defaulting to 'admin' username")
+                        username = "admin"
+                    }
                     val demobank = ensureDemobank(call)
                     /**
                      * Check here if the user has the right over the claimed 
bank account.  After
@@ -1024,8 +1042,14 @@ val sandboxApp: Application.() -> Unit = {
                     if (maybeOwnedAccount.owner != username) throw 
unauthorized(
                         "Customer '$username' has no rights over bank account 
'${maybeOwnedAccount.label}'"
                     )
+                    val req = call.receive<WithdrawalRequest>()
+                    // Check for currency consistency
+                    val amount = parseAmount(req.amount)
+                    if (amount.currency != demobank.currency) throw badRequest(
+                        "Currency ${amount.currency} differs from Demobank's: 
${demobank.currency}"
+                    )
                     val wo: TalerWithdrawalEntity = transaction { 
TalerWithdrawalEntity.new {
-                        amount = "${demobank.currency}:5"
+                        this.amount = amount.amount.toPlainString()
                         walletBankAccount = maybeOwnedAccount
                     } }
                     val baseUrl = URL(call.request.getBaseUrl())
@@ -1043,15 +1067,11 @@ val sandboxApp: Application.() -> Unit = {
                     })
                     return@post
                 }
-                // Confirm a withdrawal.
+                // Confirm a withdrawal: no basic auth, because the ID should 
be unguessable.
                 
post("/accounts/{account_name}/withdrawals/{withdrawal_id}/confirm") {
                     val withdrawalId = call.getUriComponent("withdrawal_id")
                     transaction {
-                        val wo = TalerWithdrawalEntity.find {
-                            TalerWithdrawalsTable.wopid eq 
java.util.UUID.fromString(withdrawalId)
-                        }.firstOrNull() ?: throw SandboxError(
-                            HttpStatusCode.NotFound, "Withdrawal operation 
$withdrawalId not found."
-                        )
+                        val wo = getWithdrawalOperation(withdrawalId)
                         if (wo.aborted) throw SandboxError(
                             HttpStatusCode.Conflict,
                             "Cannot confirm an aborted withdrawal."
@@ -1066,7 +1086,7 @@ val sandboxApp: Application.() -> Unit = {
                                 "Cannot withdraw without an exchange."
                             )
                         )
-                        if (!wo.transferDone) {
+                        if (!wo.confirmationDone) {
                             // Need the exchange bank account!
                             wireTransfer(
                                 debitAccount = wo.walletBankAccount,
@@ -1075,15 +1095,25 @@ val sandboxApp: Application.() -> Unit = {
                                 subject = wo.reservePub ?: throw 
internalServerError(
                                     "Cannot transfer funds without reserve 
public key."
                                 ),
+                                // provide the currency.
                                 demoBank = ensureDemobank(call)
                             )
-                            wo.transferDone = true
+                            wo.confirmationDone = true
                         }
-                        wo.transferDone
+                        wo.confirmationDone
                     }
                     call.respond(object {})
                     return@post
                 }
+
+                
post("/accounts/{account_name}/withdrawals/{withdrawal_id}/abort") {
+                    val withdrawalId = call.getUriComponent("withdrawal_id")
+                    val operation = getWithdrawalOperation(withdrawalId)
+                    if (operation.confirmationDone) throw conflict("Cannot 
abort paid withdrawal.")
+                    transaction { operation.aborted = true }
+                    call.respond(object {})
+                    return@post
+                }
                 // Bank account basic information.
                 get("/accounts/{account_name}") {
                     val username = call.request.basicAuth()
diff --git a/util/src/main/kotlin/HTTP.kt b/util/src/main/kotlin/HTTP.kt
index 9fc12f2..70f6631 100644
--- a/util/src/main/kotlin/HTTP.kt
+++ b/util/src/main/kotlin/HTTP.kt
@@ -68,6 +68,14 @@ fun badRequest(msg: String): UtilError {
     )
 }
 
+fun conflict(msg: String): UtilError {
+    return UtilError(
+        HttpStatusCode.Conflict,
+        msg,
+        ec = LibeufinErrorCode.LIBEUFIN_EC_NONE
+    )
+}
+
 /**
  * Get the base URL of a request; handles proxied case.
  */
diff --git a/util/src/main/kotlin/amounts.kt b/util/src/main/kotlin/amounts.kt
index 19446db..aaead15 100644
--- a/util/src/main/kotlin/amounts.kt
+++ b/util/src/main/kotlin/amounts.kt
@@ -28,3 +28,10 @@ val re = Regex("^([0-9]+(\\.[0-9]+)?)$")
 fun validatePlainAmount(plainAmount: String): Boolean {
     return re.matches(plainAmount)
 }
+
+fun parseAmount(amount: String): AmountWithCurrency {
+    val match = Regex("([A-Z]+):([0-9]+(\\.[0-9]+)?)").find(amount) ?: throw
+    EbicsProtocolError(HttpStatusCode.BadRequest, "invalid amount: $amount")
+    val (currency, number) = match.destructured
+    return AmountWithCurrency(currency, Amount(number))
+}
diff --git a/util/src/main/kotlin/strings.kt b/util/src/main/kotlin/strings.kt
index e44b9a2..c7253f7 100644
--- a/util/src/main/kotlin/strings.kt
+++ b/util/src/main/kotlin/strings.kt
@@ -122,13 +122,6 @@ fun parseDecimal(decimalStr: String): BigDecimal {
     }
 }
 
-fun parseAmount(amount: String): AmountWithCurrency {
-    val match = Regex("([A-Z]+):([0-9]+(\\.[0-9]+)?)").find(amount) ?: throw
-    EbicsProtocolError(HttpStatusCode.BadRequest, "invalid amount: $amount")
-    val (currency, number) = match.destructured
-    return AmountWithCurrency(currency, Amount(number))
-}
-
 fun getRandomString(length: Int): String {
     val allowedChars = ('A' .. 'Z') + ('0' .. '9')
     return (1 .. length)

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