gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] 02/02: Setting JSON request parser as the default.


From: gnunet
Subject: [libeufin] 02/02: Setting JSON request parser as the default.
Date: Mon, 14 Feb 2022 10:16:08 +0100

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

ms pushed a commit to branch master
in repository libeufin.

commit 5d537b70396d9ef4d3f0982efd9d0bd0e4839a28
Author: ms <ms@taler.net>
AuthorDate: Mon Feb 14 10:15:29 2022 +0100

    Setting JSON request parser as the default.
---
 .../tech/libeufin/nexus/ebics/EbicsClient.kt       |  5 ++-
 .../tech/libeufin/sandbox/EbicsProtocolBackend.kt  | 48 ++++++++++++++--------
 .../src/main/kotlin/tech/libeufin/sandbox/Main.kt  | 48 +++++++++-------------
 .../tech/libeufin/sandbox/XMLEbicsConverter.kt     | 33 +++++++++------
 4 files changed, 75 insertions(+), 59 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
index f0242ceb..fdacdeb7 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsClient.kt
@@ -24,8 +24,9 @@ package tech.libeufin.nexus.ebics
 
 import io.ktor.client.HttpClient
 import io.ktor.client.features.*
-import io.ktor.client.request.post
-import io.ktor.http.HttpStatusCode
+import io.ktor.client.request.*
+import io.ktor.http.*
+import io.ktor.util.*
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
 import tech.libeufin.nexus.NexusError
diff --git 
a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
index 0594e934..cda505ab 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -23,7 +23,7 @@ package tech.libeufin.sandbox
 import io.ktor.application.*
 import io.ktor.http.ContentType
 import io.ktor.http.HttpStatusCode
-import io.ktor.request.receiveText
+import io.ktor.request.*
 import io.ktor.response.respond
 import io.ktor.response.respondText
 import io.ktor.util.AttributeKey
@@ -33,8 +33,6 @@ import org.jetbrains.exposed.sql.*
 import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
 import org.jetbrains.exposed.sql.statements.api.ExposedBlob
 import org.jetbrains.exposed.sql.transactions.transaction
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
 import org.w3c.dom.Document
 import tech.libeufin.util.*
 import tech.libeufin.util.XMLUtil.Companion.signEbicsResponse
@@ -46,8 +44,6 @@ import tech.libeufin.util.ebics_s001.UserSignatureData
 import java.math.BigDecimal
 import java.security.interfaces.RSAPrivateCrtKey
 import java.security.interfaces.RSAPublicKey
-import java.time.Instant
-import java.time.LocalDateTime
 import java.util.*
 import java.util.zip.DeflaterInputStream
 import java.util.zip.InflaterInputStream
@@ -124,6 +120,21 @@ suspend fun respondEbicsTransfer(
     errorText: String,
     errorCode: String
 ) {
+    /**
+     * Because this handler runs for any error, it could
+     * handle the case where the Ebics host ID is unknown due
+     * to an invalid request.  Recall: Sandbox is multi-host, and
+     * which Ebics host was requested belongs to the request document.
+     *
+     * Therefore, because any (? Please verify!) Ebics response
+     * should speak for one Ebics host, we won't respond any Ebics
+     * type when the Ebics host ID remains unknown due to invalid
+     * request.  Instead, we'll respond plain text:
+     */
+    if (!call.attributes.contains(EbicsHostIdAttribute)) {
+        call.respondText("Invalid document.", status = 
HttpStatusCode.BadRequest)
+        return
+    }
     val resp = EbicsResponse.createForUploadWithError(
         errorText,
         errorCode,
@@ -935,7 +946,7 @@ private suspend fun ApplicationCall.handleEbicsHpb(
 /**
  * Find the ebics host corresponding to the one specified in the header.
  */
-private fun ApplicationCall.ensureEbicsHost(requestHostID: String): 
EbicsHostPublicInfo {
+private fun ensureEbicsHost(requestHostID: String): EbicsHostPublicInfo {
     return transaction {
         val ebicsHost =
             EbicsHostEntity.find { EbicsHostsTable.hostID.upperCase() eq 
requestHostID.uppercase(Locale.getDefault()) }.firstOrNull()
@@ -952,22 +963,19 @@ private fun 
ApplicationCall.ensureEbicsHost(requestHostID: String): EbicsHostPub
         )
     }
 }
-
-private suspend fun ApplicationCall.receiveEbicsXml(): Document {
-    val body: String = receiveText()
-    logger.debug("Data received: $body")
-    val requestDocument: Document? = XMLUtil.parseStringIntoDom(body)
+fun receiveEbicsXmlInternal(xmlData: String): Document {
+    logger.debug("Data received: $xmlData")
+    val requestDocument: Document? = XMLUtil.parseStringIntoDom(xmlData)
     if (requestDocument == null || 
(!XMLUtil.validateFromDom(requestDocument))) {
         println("Problematic document was: $requestDocument")
         throw EbicsInvalidXmlError()
     }
-    val requestedHostID = requestDocument.getElementsByTagName("HostID")
-    this.attributes.put(
-        EbicsHostIdAttribute,
-        requestedHostID.item(0).textContent
-    )
     return requestDocument
 }
+suspend fun ApplicationCall.receiveEbicsXml(): Document {
+    val body: String = receiveText()
+    return receiveEbicsXmlInternal(body)
+}
 
 private fun makePartnerInfo(subscriber: EbicsSubscriberEntity): 
EbicsTypes.PartnerInfo {
     val bankAccount = getBankAccountFromSubscriber(subscriber)
@@ -1324,7 +1332,13 @@ private fun makeRequestContext(requestObject: 
EbicsRequest): RequestContext {
 }
 
 suspend fun ApplicationCall.ebicsweb() {
-    val requestDocument = receiveEbicsXml()
+    val requestDocument = this.request.call.receive<Document>()
+    val requestedHostID = requestDocument.getElementsByTagName("HostID")
+    this.attributes.put(
+        EbicsHostIdAttribute,
+        requestedHostID.item(0).textContent
+    )
+    // val requestDocument = receiveEbicsXml()
     logger.info("Processing ${requestDocument.documentElement.localName}")
     when (requestDocument.documentElement.localName) {
         "ebicsUnsecuredRequest" -> {
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
index ca4a0db3..caf8167a 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -370,11 +370,6 @@ inline fun <reified T> Document.toObject(): T {
     return m.unmarshal(this, T::class.java).value
 }
 
-fun BigDecimal.signToString(): String {
-    return if (this.signum() > 0) "+" else ""
-    // minus sign is added by default already.
-}
-
 fun ensureNonNull(param: String?): String {
     return param ?: throw SandboxError(
         HttpStatusCode.BadRequest, "Bad ID given: $param"
@@ -426,25 +421,22 @@ val sandboxApp: Application.() -> Unit = {
         logger.info("Enabling CORS (assuming no endpoint uses cookies).")
         allowCredentials = true
     }
-    install(Authentication) {
-        // Web-based authentication for Bank customers.
-        form("auth-form") {
-            userParamName = "username"
-            passwordParamName = "password"
-            validate { credentials ->
-                if (credentials.name == "test") {
-                    UserIdPrincipal(credentials.name)
-                } else {
-                    null
-                }
-            }
-        }
-    }
     install(ContentNegotiation) {
-        jackson {
+        register(ContentType.Text.Xml, XMLEbicsConverter())
+        /**
+         * Content type "text" must go to the XML parser
+         * because Nexus can't set explicitly the Content-Type
+         * (see https://github.com/ktorio/ktor/issues/1127) to
+         * "xml" and the request made gets somehow assigned the
+         * "text/plain" type:  */
+        register(ContentType.Text.Plain, XMLEbicsConverter())
+        /**
+         * Make jackson the default parser.  It runs also when
+         * the Content-Type request header is missing. */
+        jackson(contentType = ContentType.Any) {
             
enable(com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT)
             setDefaultPrettyPrinter(DefaultPrettyPrinter().apply {
-                
indentArraysWith(com.fasterxml.jackson.core.util.DefaultPrettyPrinter.FixedSpaceIndenter.instance)
+                
indentArraysWith(DefaultPrettyPrinter.FixedSpaceIndenter.instance)
                 indentObjectsWith(DefaultIndenter("  ", "\n"))
             })
             registerModule(KotlinModule(nullisSameAsDefault = true))
@@ -493,8 +485,8 @@ val sandboxApp: Application.() -> Unit = {
             logger.error("Exception while handling '${call.request.uri}'", 
cause)
             call.respondText(
                 "Internal server error.",
-                io.ktor.http.ContentType.Text.Plain,
-                io.ktor.http.HttpStatusCode.InternalServerError
+                ContentType.Text.Plain,
+                HttpStatusCode.InternalServerError
             )
         }
     }
@@ -530,7 +522,10 @@ val sandboxApp: Application.() -> Unit = {
     routing {
 
         get("/") {
-            call.respondText("Hello, this is the Sandbox\n", 
ContentType.Text.Plain)
+            call.respondText(
+                "Hello, this is the Sandbox\n",
+                ContentType.Text.Plain
+            )
         }
 
         // Respond with the last statement of the requesting account.
@@ -937,9 +932,7 @@ val sandboxApp: Application.() -> Unit = {
             }
             catch (e: Exception) {
                 logger.error(e)
-                if (e !is EbicsRequestError) {
-                    throw EbicsProcessingError("Unmanaged error: $e")
-                }
+                throw EbicsProcessingError("Unmanaged error: $e")
             }
             return@post
         }
@@ -990,7 +983,6 @@ val sandboxApp: Application.() -> Unit = {
             call.respond(getJsonFromDemobankConfig(demobank))
             return@get
         }
-
         route("/demobanks/{demobankid}") {
 
             // NOTE: TWG assumes that username == bank account label.
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLEbicsConverter.kt 
b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLEbicsConverter.kt
index 5dc23af6..64166daa 100644
--- a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLEbicsConverter.kt
+++ b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLEbicsConverter.kt
@@ -7,16 +7,22 @@ import io.ktor.http.content.*
 import io.ktor.request.*
 import io.ktor.response.*
 import io.ktor.util.pipeline.*
+import io.ktor.utils.io.*
+import io.ktor.utils.io.jvm.javaio.*
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import tech.libeufin.util.XMLUtil
 import java.io.OutputStream
+import java.nio.channels.ByteChannel
 
-public class EbicsConverter : ContentConverter {
-    override suspend fun convertForReceive(context: 
PipelineContext<ApplicationReceiveRequest, ApplicationCall>): Any {
-        return context.context.receiveEbicsXml()
+class XMLEbicsConverter : ContentConverter {
+    override suspend fun convertForReceive(
+        context: PipelineContext<ApplicationReceiveRequest, ApplicationCall>): 
Any? {
+        val value = context.subject.value as? ByteReadChannel ?: return null
+        return withContext(Dispatchers.IO) {
+            receiveEbicsXmlInternal(value.toInputStream().reader().readText())
+        }
     }
-
     override suspend fun convertForSend(
         context: PipelineContext<Any, ApplicationCall>,
         contentType: ContentType,
@@ -25,16 +31,19 @@ public class EbicsConverter : ContentConverter {
         val conv = try {
             XMLUtil.convertJaxbToString(value)
         } catch (e: Exception) {
-            logger.warn("Could not convert XML to string with custom 
converter.")
+            /**
+             * Not always a error: the content negotiation might have
+             * only checked if this handler could convert the response.
+             */
+            logger.debug("Could not convert XML to string with custom 
converter.")
             return null
         }
         return OutputStreamContent({
-            suspend fun writeAsync(out: OutputStream) {
-                withContext(Dispatchers.IO) {
-                    out.write(conv.toByteArray())
-                }
-            }
-            writeAsync(this)
-        })
+            val out = this;
+            withContext(Dispatchers.IO) {
+                out.write(conv.toByteArray())
+            }},
+            contentType.withCharset(context.call.suitableCharset())
+        )
     }
 }
\ No newline at end of file

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