libeufin

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

commit 94bb65e965a78cf9c05e1b596bb3e7b6dc2e2ebc
parent 5413a5f64cd06ac5b420e4c9be07de241fe886a6
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date:   Mon,  9 Mar 2020 14:43:16 +0100

unifying error response types

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt | 20++++++++++----------
Mnexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 2+-
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 46++++++++++++++++++----------------------------
3 files changed, 29 insertions(+), 39 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsClient.kt @@ -16,7 +16,7 @@ suspend inline fun HttpClient.postToBank(url: String, body: String): String { } ) } catch (e: Exception) { - throw UnreachableBankError(HttpStatusCode.InternalServerError) + throw NexusError(HttpStatusCode.InternalServerError, "Cannot reach the bank") } return response } @@ -57,7 +57,7 @@ suspend fun doEbicsDownloadTransaction( // Success, nothing to do! } else -> { - throw ProtocolViolationError("unexpected return code ${initResponse.technicalReturnCode}") + throw NexusError(HttpStatusCode.InternalServerError, "unexpected return code ${initResponse.technicalReturnCode}") } } @@ -71,13 +71,13 @@ suspend fun doEbicsDownloadTransaction( } val transactionID = - initResponse.transactionID ?: throw ProtocolViolationError("initial response must contain transaction ID") + initResponse.transactionID ?: throw NexusError(HttpStatusCode.InternalServerError, "initial response must contain transaction ID") val encryptionInfo = initResponse.dataEncryptionInfo - ?: throw ProtocolViolationError("initial response did not contain encryption info") + ?: throw NexusError(HttpStatusCode.InternalServerError, "initial response did not contain encryption info") val initOrderDataEncChunk = initResponse.orderDataEncChunk - ?: throw ProtocolViolationError("initial response for download transaction does not contain data transfer") + ?: throw NexusError(HttpStatusCode.InternalServerError,"initial response for download transaction does not contain data transfer") payloadChunks.add(initOrderDataEncChunk) @@ -95,7 +95,7 @@ suspend fun doEbicsDownloadTransaction( EbicsReturnCode.EBICS_DOWNLOAD_POSTPROCESS_DONE -> { } else -> { - throw ProtocolViolationError("unexpected return code") + throw NexusError(HttpStatusCode.InternalServerError,"unexpected return code") } } return EbicsDownloadSuccessResult(respPayload) @@ -110,7 +110,7 @@ suspend fun doEbicsUploadTransaction( orderParams: EbicsOrderParams ) { if (subscriberDetails.bankEncPub == null) { - throw InvalidSubscriberStateError("bank encryption key unknown, request HPB first") + throw NexusError(HttpStatusCode.BadRequest, "bank encryption key unknown, request HPB first") } val preparedUploadData = prepareUploadPayload(subscriberDetails, payload) val req = createEbicsRequestForUploadInitialization(subscriberDetails, orderType, orderParams, preparedUploadData) @@ -118,11 +118,11 @@ suspend fun doEbicsUploadTransaction( val initResponse = parseAndValidateEbicsResponse(subscriberDetails, responseStr) if (initResponse.technicalReturnCode != EbicsReturnCode.EBICS_OK) { - throw ProtocolViolationError("unexpected return code") + throw NexusError(HttpStatusCode.InternalServerError, reason = "unexpected return code") } val transactionID = - initResponse.transactionID ?: throw ProtocolViolationError("init response must have transaction ID") + initResponse.transactionID ?: throw NexusError(HttpStatusCode.InternalServerError,"init response must have transaction ID") logger.debug("INIT phase passed!") /* now send actual payload */ @@ -145,7 +145,7 @@ suspend fun doEbicsUploadTransaction( EbicsReturnCode.EBICS_OK -> { } else -> { - throw ProtocolViolationError("unexpected return code") + throw NexusError(HttpStatusCode.InternalServerError,"unexpected return code") } } } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt @@ -25,5 +25,5 @@ fun chunkString(input: String): String { } fun expectId(param: String?): String { - return param ?: throw NotAnIdError(HttpStatusCode.BadRequest) + return param ?: throw NexusError(HttpStatusCode.BadRequest, "Bad ID given") } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -58,6 +58,7 @@ import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* import javax.crypto.EncryptedPrivateKeyInfo +import javax.print.attribute.standard.JobStateReason import javax.sql.rowset.serial.SerialBlob fun testData() { @@ -82,6 +83,7 @@ fun testData() { } } +data class NexusError(val statusCode: HttpStatusCode, val reason: String) : Exception(reason) data class NotAnIdError(val statusCode: HttpStatusCode) : Exception("String ID not convertible in number") data class BankKeyMissing(val statusCode: HttpStatusCode) : Exception("Impossible operation: bank keys are missing") data class SubscriberNotFoundError(val statusCode: HttpStatusCode) : Exception("Subscriber not found in database") @@ -102,9 +104,7 @@ val logger: Logger = LoggerFactory.getLogger("tech.libeufin.nexus") fun getSubscriberEntityFromId(id: String): EbicsSubscriberEntity { return transaction { - EbicsSubscriberEntity.findById(id) ?: throw SubscriberNotFoundError( - HttpStatusCode.NotFound - ) + EbicsSubscriberEntity.findById(id) ?: throw NexusError(HttpStatusCode.NotFound, "Subscriber not found from id '$id'") } } @@ -112,7 +112,7 @@ fun getBankAccountDetailsFromAcctid(id: String): EbicsAccountInfoElement { return transaction { val bankAccount = EbicsAccountInfoEntity.find { EbicsAccountsInfoTable.id eq id - }.firstOrNull() ?: throw BankAccountNotFoundError(HttpStatusCode.NotFound) + }.firstOrNull() ?: throw NexusError(HttpStatusCode.NotFound, "Bank account not found from account id '$id'") EbicsAccountInfoElement( accountId = id, accountHolderName = bankAccount.accountHolder, @@ -123,7 +123,7 @@ fun getBankAccountDetailsFromAcctid(id: String): EbicsAccountInfoElement { } fun getSubscriberDetailsFromBankAccount(bankAccountId: String): EbicsClientSubscriberDetails { return transaction { - val accountInfo = EbicsAccountInfoEntity.findById(bankAccountId) ?: throw Exception("Bank account ($bankAccountId) not managed by Nexus") + val accountInfo = EbicsAccountInfoEntity.findById(bankAccountId) ?: throw NexusError(HttpStatusCode.NotFound, "Bank account ($bankAccountId) not managed by Nexus") logger.debug("Mapping bank account: ${bankAccountId}, to customer: ${accountInfo.subscriber.id.value}") getSubscriberDetailsFromId(accountInfo.subscriber.id.value) } @@ -131,11 +131,7 @@ fun getSubscriberDetailsFromBankAccount(bankAccountId: String): EbicsClientSubsc fun getSubscriberDetailsFromId(id: String): EbicsClientSubscriberDetails { return transaction { - val subscriber = EbicsSubscriberEntity.findById( - id - ) ?: throw SubscriberNotFoundError( - HttpStatusCode.NotFound - ) + val subscriber = EbicsSubscriberEntity.findById(id) ?: throw NexusError(HttpStatusCode.NotFound, "subscriber not found from id '$id'") var bankAuthPubValue: RSAPublicKey? = null if (subscriber.bankAuthenticationPublicKey != null) { bankAuthPubValue = CryptoUtil.loadRsaPublicKey( @@ -237,17 +233,13 @@ fun createPain001document(pain001Entity: Pain001Entity): String { } element("DbtrAcct/Id/IBAN") { text(transaction { - EbicsAccountInfoEntity.findById(pain001Entity.debtorAccount)?.iban ?: throw Exception( - "Debtor IBAN not found in database" - ) + EbicsAccountInfoEntity.findById(pain001Entity.debtorAccount)?.iban ?: throw NexusError(HttpStatusCode.NotFound,"Debtor IBAN not found in database") }) } element("DbtrAgt/FinInstnId/BIC") { text(transaction { - EbicsAccountInfoEntity.findById(pain001Entity.debtorAccount)?.bankCode ?: throw Exception( - "Debtor BIC not found in database" - ) + EbicsAccountInfoEntity.findById(pain001Entity.debtorAccount)?.bankCode ?: throw NexusError(HttpStatusCode.NotFound,"Debtor BIC not found in database") }) } @@ -499,7 +491,7 @@ fun main() { val accountinfo = EbicsAccountInfoEntity.findById(acctid) val subscriber = EbicsSubscriberEntity.findById(subscriberId) if (accountinfo?.subscriber != subscriber) { - throw Exception("Claimed account doesn't belong to POSTer!") + throw NexusError(HttpStatusCode.BadRequest, "Claimed bank account '$acctid' doesn't belong to subscriber '$subscriberId'!") } } val pain001data = call.receive<Pain001Data>() @@ -551,7 +543,7 @@ fun main() { val (painDoc, debtorAccount) = transaction { val entity = Pain001Entity.find { Pain001Table.submitted eq false - }.firstOrNull() ?: throw Exception("No ready payments found") + }.firstOrNull() ?: throw NexusError(HttpStatusCode.Accepted, reason = "No ready payments found") kotlin.Pair(createPain001document(entity), entity.debtorAccount) } logger.info("Processing payment for bank account: ${debtorAccount}") @@ -1069,7 +1061,7 @@ fun main() { ) val resp = parseAndDecryptEbicsKeyManagementResponse(subscriberData, responseStr) if (resp.technicalReturnCode != EbicsReturnCode.EBICS_OK) { - throw EbicsError("Unexpected INI response code: ${resp.technicalReturnCode}") + throw NexusError(HttpStatusCode.InternalServerError,"Unexpected INI response code: ${resp.technicalReturnCode}") } call.respondText("Bank accepted signature key\n", ContentType.Text.Plain, HttpStatusCode.OK) return@post @@ -1085,7 +1077,7 @@ fun main() { ) val resp = parseAndDecryptEbicsKeyManagementResponse(subscriberData, responseStr) if (resp.technicalReturnCode != EbicsReturnCode.EBICS_OK) { - throw EbicsError("Unexpected HIA response code: ${resp.technicalReturnCode}") + throw NexusError(HttpStatusCode.InternalServerError,"Unexpected HIA response code: ${resp.technicalReturnCode}") } call.respondText( "Bank accepted authentication and encryption keys\n", @@ -1123,7 +1115,7 @@ fun main() { } catch (e: Exception) { e.printStackTrace() logger.info("Restoring keys failed, probably due to wrong passphrase") - throw BadBackup(HttpStatusCode.BadRequest) + throw NexusError(HttpStatusCode.BadRequest, reason = "Bad backup given") } logger.info("Restoring keys, creating new user: $id") try { @@ -1192,12 +1184,12 @@ fun main() { iban = when (val firstAccount = it.accountNumberList?.get(0)) { is EbicsTypes.GeneralAccountNumber -> firstAccount.value is EbicsTypes.NationalAccountNumber -> firstAccount.value - else -> throw UnknownBankAccountType(HttpStatusCode.NotFound) + else -> throw NexusError(HttpStatusCode.NotFound, reason = "Unknown bank account type because of IBAN type") } bankCode = when (val firstBankCode = it.bankCodeList?.get(0)) { is EbicsTypes.GeneralBankCode -> firstBankCode.value is EbicsTypes.NationalBankCode -> firstBankCode.value - else -> throw UnknownBankAccountType(HttpStatusCode.NotFound) + else -> throw NexusError(HttpStatusCode.NotFound, reason = "Unknown bank account type because of BIC type") } } } @@ -1223,9 +1215,7 @@ fun main() { val id = expectId(call.parameters["id"]) val body = call.receive<EbicsBackupRequestJson>() val response = transaction { - val subscriber = EbicsSubscriberEntity.findById(id) ?: throw SubscriberNotFoundError( - HttpStatusCode.NotFound - ) + val subscriber = EbicsSubscriberEntity.findById(id) ?: throw NexusError(HttpStatusCode.NotFound, "Subscriber '$id' not found") EbicsKeysBackupJson( userID = subscriber.userID, hostID = subscriber.hostID, @@ -1286,12 +1276,12 @@ fun main() { val response = parseAndDecryptEbicsKeyManagementResponse(subscriberDetails, responseStr) val orderData = - response.orderData ?: throw ProtocolViolationError("expected order data in HPB response") + response.orderData ?: throw NexusError(HttpStatusCode.InternalServerError, "HPB response has no order data") val hpbData = parseEbicsHpbOrder(orderData) // put bank's keys into database. transaction { - val subscriber = EbicsSubscriberEntity.findById(id) ?: throw InvalidSubscriberStateError() + val subscriber = EbicsSubscriberEntity.findById(id) ?: throw NexusError(HttpStatusCode.BadRequest, "Invalid subscriber state") subscriber.bankAuthenticationPublicKey = SerialBlob(hpbData.authenticationPubKey.encoded) subscriber.bankEncryptionPublicKey = SerialBlob(hpbData.encryptionPubKey.encoded) }