libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

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:
Msandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt | 14++++++++++++--
Msandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt | 47++++++++++++++++++++++++++++++-----------------
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") {