[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.