commit 3a7e1ec44f643ffd91a23edd925ccb2bdf248e31
parent b556bd1b92b18f56eaf38d137d1792d800c13e88
Author: MS <ms@taler.net>
Date: Thu, 8 Dec 2022 11:02:38 +0100
EBICS error management.
Fix error responded along a subscriber
not being found to a INI request.
Diffstat:
2 files changed, 42 insertions(+), 19 deletions(-)
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -95,6 +95,11 @@ class EbicsSubscriberStateError : EbicsRequestError(
"[EBICS_INVALID_USER_OR_USER_STATE] Subscriber unknown or subscriber state inadmissible",
"091002"
)
+// hint should mention at least the userID
+class EbicsUserUnknown(hint: String) : EbicsRequestError(
+ "[EBICS_USER_UNKNOWN] $hint",
+ "091003"
+)
open class EbicsKeyManagementError(val errorText: String, val errorCode: String) :
Exception("EBICS key management error: $errorText ($errorCode)")
@@ -890,8 +895,13 @@ private suspend fun ApplicationCall.handleEbicsIni(header: EbicsUnsecuredRequest
val ebicsSubscriber =
findEbicsSubscriber(header.static.partnerID, header.static.userID, header.static.systemID)
if (ebicsSubscriber == null) {
- logger.warn("ebics subscriber ('${header.static.partnerID}' / '${header.static.userID}' / '${header.static.systemID}') not found")
- throw EbicsInvalidRequestError()
+ logger.warn(
+ "ebics subscriber, userID: ${header.static.userID}" +
+ ", partnerID: ${header.static.partnerID}" +
+ ", systemID: ${header.static.systemID}," +
+ "not found"
+ )
+ throw EbicsUserUnknown(header.static.userID)
}
when (ebicsSubscriber.state) {
SubscriberState.NEW -> {}
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -54,6 +54,7 @@ import com.github.ajalt.clikt.parameters.options.*
import com.github.ajalt.clikt.parameters.types.int
import execThrowableOrTerminate
import io.ktor.application.*
+import io.ktor.client.statement.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.jackson.*
@@ -87,12 +88,14 @@ const val SANDBOX_DB_ENV_VAR_NAME = "LIBEUFIN_SANDBOX_DB_CONNECTION"
private val adminPassword: String? = System.getenv("LIBEUFIN_SANDBOX_ADMIN_PASSWORD")
var WITH_AUTH = true // Needed by helpers too, hence not making it private.
+// Internal error type.
data class SandboxError(
val statusCode: HttpStatusCode,
val reason: String,
val errorCode: LibeufinErrorCode? = null
) : Exception(reason)
+// HTTP response error type.
data class SandboxErrorJson(val error: SandboxErrorDetailJson)
data class SandboxErrorDetailJson(val type: String, val description: String)
@@ -166,7 +169,9 @@ class Config : CliktCommand(
execThrowableOrTerminate {
dbCreateTables(dbConnString)
transaction {
- val maybeDemobank = BankAccountEntity.find(BankAccountsTable.label eq "bank").firstOrNull()
+ val maybeDemobank = BankAccountEntity.find(
+ BankAccountsTable.label eq "bank"
+ ).firstOrNull()
if (showOption) {
if (maybeDemobank != null) {
val ret = ObjectMapper()
@@ -503,14 +508,17 @@ val sandboxApp: Application.() -> Unit = {
jackson(contentType = ContentType.Any) { setJsonHandler(this) }
}
install(StatusPages) {
+ // Bank's fault: it should check the operands. Respond 500
exception<ArithmeticException> { cause ->
- logger.error("Exception while handling '${call.request.uri}', ${cause.message}")
- call.respondText(
- "Invalid arithmetic attempted.",
- ContentType.Text.Plain,
- // here is always the bank's fault, as it should always check
- // the operands.
- HttpStatusCode.InternalServerError
+ logger.error("Exception while handling '${call.request.uri}', ${cause.stackTraceToString()}")
+ call.respond(
+ HttpStatusCode.InternalServerError,
+ SandboxErrorJson(
+ error = SandboxErrorDetailJson(
+ type = "sandbox-error",
+ description = cause.message ?: "Bank's error: arithmetic exception."
+ )
+ )
)
}
exception<SandboxError> { cause ->
@@ -537,18 +545,23 @@ val sandboxApp: Application.() -> Unit = {
)
)
}
+ // Catch-all error, respond 500 because the bank didn't handle it.
+ exception<Throwable> { cause ->
+ logger.error("Exception while handling '${call.request.uri}'", cause.stackTrace)
+ call.respond(
+ HttpStatusCode.InternalServerError,
+ SandboxErrorJson(
+ error = SandboxErrorDetailJson(
+ type = "sandbox-error",
+ description = cause.message ?: "Bank's error: unhandled exception."
+ )
+ )
+ )
+ }
exception<EbicsRequestError> { e ->
logger.info("Handling EbicsRequestError: ${e.message}")
respondEbicsTransfer(call, e.errorText, e.errorCode)
}
- exception<Throwable> { cause ->
- logger.error("Exception while handling '${call.request.uri}'", cause.message)
- call.respondText(
- "Internal server error.",
- ContentType.Text.Plain,
- HttpStatusCode.InternalServerError
- )
- }
}
intercept(ApplicationCallPipeline.Setup) {
val ac: ApplicationCall = call
@@ -1564,7 +1577,7 @@ val sandboxApp: Application.() -> Unit = {
}
route("/ebics") {
/**
- * Associate a bank account to one EBICS subscriber.
+ * Associate an existing bank account to one EBICS subscriber.
* If the subscriber is not found, it is created.
*/
post("/subscribers") {