[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libeufin] branch master updated (7e4b86e6 -> 6de75405)
From: |
gnunet |
Subject: |
[libeufin] branch master updated (7e4b86e6 -> 6de75405) |
Date: |
Fri, 08 Jul 2022 16:48:01 +0200 |
This is an automated email from the git hooks/post-receive script.
ms pushed a change to branch master
in repository libeufin.
from 7e4b86e6 decrease log verbosity
new 24838e2d (re)implement /admin/add-incoming
new 6de75405 more on /admin/add-incoming
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:
cli/bin/libeufin-cli | 8 +-
nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt | 106 ++++++++++++++++++++-
.../tech/libeufin/nexus/bankaccount/BankAccount.kt | 2 +-
.../kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt | 2 +-
.../src/main/kotlin/tech/libeufin/sandbox/Main.kt | 12 ++-
util/src/main/kotlin/HTTP.kt | 7 ++
6 files changed, 128 insertions(+), 9 deletions(-)
diff --git a/cli/bin/libeufin-cli b/cli/bin/libeufin-cli
index 5ccb0663..c6310788 100755
--- a/cli/bin/libeufin-cli
+++ b/cli/bin/libeufin-cli
@@ -1333,12 +1333,18 @@ def sandbox_demobank_delete(obj, bank_account):
default="",
help="Person name",
)
+@click.option(
+ "--iban",
+ help="Uses this IBAN, instead of a random one.",
+)
@click.pass_obj
-def sandbox_demobank_register(obj, public, name):
+def sandbox_demobank_register(obj, public, name, iban):
url = obj.access_api_url ("/testing/register")
req = dict(username=obj.username, password=obj.password, isPublic=public)
if name != "":
req.update(name=name)
+ if iban:
+ req.update(iban=iban)
try:
resp = post(
url,
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
index bdb0969e..202453ed 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
@@ -22,24 +22,28 @@ package tech.libeufin.nexus
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.ktor.application.ApplicationCall
import io.ktor.application.call
+import io.ktor.client.*
+import io.ktor.client.features.*
+import io.ktor.client.request.*
import io.ktor.content.TextContent
-import io.ktor.http.ContentType
-import io.ktor.http.HttpStatusCode
+import io.ktor.http.*
import io.ktor.request.receive
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 io.ktor.util.*
import org.jetbrains.exposed.dao.Entity
import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import tech.libeufin.nexus.bankaccount.addPaymentInitiation
+import tech.libeufin.nexus.bankaccount.fetchBankAccountTransactions
import tech.libeufin.nexus.iso20022.*
import tech.libeufin.nexus.server.*
import tech.libeufin.util.*
-import java.net.URLEncoder
+import java.net.URL
import kotlin.math.abs
import kotlin.math.min
@@ -56,7 +60,7 @@ data class TalerTransferRequest(
data class TalerTransferResponse(
/**
- * Point in time when the nexus put the payment instruction into the
database.
+ * Point in time when Nexus put the payment instruction into the database.
*/
val timestamp: GnunetTimestamp,
val row_id: Long
@@ -455,6 +459,96 @@ private suspend fun historyIncoming(call: ApplicationCall)
{
return call.respond(TextContent(customConverter(history),
ContentType.Application.Json))
}
+/**
+ * This call proxies /admin/add/incoming to the Sandbox,
+ * which is the service keeping the transactions ledger.
+ * The credentials are ASSUMED to be exchange/x (user/pass).
+ *
+ * In the future, a dedicate "add-incoming" facade should
+ * be provided, offering the mean to store the credentials
+ * at configuration time.
+ *
+ */
+private suspend fun addIncoming(call: ApplicationCall) {
+ val facadeId = ensureNonNull(call.parameters["fcid"])
+ val currentBody = call.receive<String>()
+ val fromDb = transaction {
+ val f = FacadeEntity.findByName(facadeId) ?: throw notFound("facade
$facadeId not found")
+ val state = FacadeStateEntity.find {
+ FacadeStateTable.facade eq f.id
+ }.firstOrNull() ?: throw internalServerError("facade $facadeId has no
state!")
+ val conn = NexusBankConnectionEntity.findByName(state.bankConnection)
?: throw internalServerError(
+ "state of facade $facadeId has no bank connection!"
+ )
+ val ebicsData = NexusEbicsSubscribersTable.select {
+ NexusEbicsSubscribersTable.nexusBankConnection eq conn.id
+ }.firstOrNull() ?: throw internalServerError(
+ "Connection '${conn.connectionId}' doesn't have EBICS"
+ )
+ // Resort Sandbox URL from EBICS endpoint.
+ val sandboxUrl = URL(ebicsData[NexusEbicsSubscribersTable.ebicsURL])
+ // NOTE: the exchange username must be 'exchange', at the Sandbox.
+ return@transaction Pair(url {
+ protocol = URLProtocol(sandboxUrl.protocol, 80)
+ host = sandboxUrl.host
+ if (sandboxUrl.port != 80) port = sandboxUrl.port
+ path(
+ "demobanks",
+ "default",
+ "taler-wire-gateway",
+ "exchange",
+ "admin",
+ "add-incoming"
+ )
+ }, state.bankAccount
+ )
+ }
+ val client = HttpClient { followRedirects = true }
+ try {
+ client.post<String>(
+ urlString = fromDb.first,
+ block = {
+ this.body = currentBody
+ this.header(
+ "Authorization",
+ buildBasicAuthLine("exchange", "x")
+ )
+ this.header("Content-Type", "application/json")
+ }
+ )
+ } catch (e: ClientRequestException) {
+ logger.error("Proxying /admin/add/incoming to the Sandbox failed: $e")
+ } catch (e: Exception) {
+ logger.error("Could not proxy /admin/add/incoming to the Sandbox: $e")
+ }
+ /**
+ * At this point, Sandbox booked the payment. Now the "row_id"
+ * value to put in the response needs to be resorted; that may
+ * be known by fetching a fresh C52 report, then let Nexus ingest
+ * the result, and finally _optimistically_ pick the latest entry
+ * in the received payments. */
+ fetchBankAccountTransactions(
+ client,
+ FetchSpecLatestJson(
+ FetchLevel.REPORT,
+ null
+ ),
+ fromDb.second
+ )
+ /**
+ * The latest incoming payment should now be found among
+ * the ingested ones.
+ */
+ val lastIncomingPayment = transaction {
+ val lastRecord = TalerIncomingPaymentEntity.all().last()
+ return@transaction Pair(lastRecord.id.value, lastRecord.timestampMs)
+ }
+ call.respond(object {
+ val row_id = lastIncomingPayment.first
+ val timestamp = GnunetTimestamp(lastIncomingPayment.second / 1000L)
+ })
+}
+
private fun getCurrency(facadeName: String): String {
return transaction {
getFacadeState(facadeName).currency
@@ -487,6 +581,10 @@ fun talerFacadeRoutes(route: Route) {
historyIncoming(call)
return@get
}
+ route.post("/admin/add-incoming") {
+ addIncoming(call)
+ return@post
+ }
route.get("") {
call.respondText("Hello, this is a Taler Facade")
return@get
diff --git
a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
index 7bd0a035..c0e20dc3 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/bankaccount/BankAccount.kt
@@ -352,7 +352,7 @@ suspend fun fetchBankAccountTransactions(
if (acct == null) {
throw NexusError(
HttpStatusCode.NotFound,
- "Account not found"
+ "Account '$accountId' not found"
)
}
val conn = acct.defaultBankConnection
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
index a3f70793..2e22a68f 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt
@@ -415,7 +415,7 @@ class EbicsBankConnectionProtocol: BankConnectionProtocol {
if (acct == null) {
throw NexusError(
HttpStatusCode.NotFound,
- "Account not found"
+ "Account '$accountId' not found"
)
}
object {
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index 67c8b371..ef97b4df 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -1069,8 +1069,8 @@ val sandboxApp: Application.() -> Unit = {
call.respond(getJsonFromDemobankConfig(demobank))
return@get
}
- route("/demobanks/{demobankid}") {
+ route("/demobanks/{demobankid}") {
// NOTE: TWG assumes that username == bank account label.
route("/taler-wire-gateway") {
post("/{exchangeUsername}/admin/add-incoming") {
@@ -1082,7 +1082,15 @@ val sandboxApp: Application.() -> Unit = {
)
}
logger.debug("TWG add-incoming passed authentication")
- val body = call.receiveJson<TWGAdminAddIncoming>()
+ val body = try {
+ call.receiveJson<TWGAdminAddIncoming>()
+ } catch (e: Exception) {
+ logger.error("/admin/add-incoming failed at parsing
the request body")
+ throw SandboxError(
+ HttpStatusCode.BadRequest,
+ "Invalid request"
+ )
+ }
transaction {
val demobank = ensureDemobank(call)
val bankAccountCredit =
getBankAccountFromLabel(username, demobank)
diff --git a/util/src/main/kotlin/HTTP.kt b/util/src/main/kotlin/HTTP.kt
index 368c2c87..387a7b05 100644
--- a/util/src/main/kotlin/HTTP.kt
+++ b/util/src/main/kotlin/HTTP.kt
@@ -153,6 +153,13 @@ fun getAuthorizationHeader(request: ApplicationRequest):
String {
)
}
+// Builds the Authorization:-header value, given the credentials.
+fun buildBasicAuthLine(username: String, password: String): String {
+ val ret = "Basic "
+ val cred = "$username:$password"
+ val enc = bytesToBase64(cred.toByteArray(Charsets.UTF_8))
+ return ret+enc
+}
/**
* This helper function parses a Authorization:-header line, decode the
credentials
* and returns a pair made of username and hashed (sha256) password. The
hashed value
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [libeufin] branch master updated (7e4b86e6 -> 6de75405),
gnunet <=