diff options
-rwxr-xr-x | integration-tests/test-ebics-highlevel.py | 2 | ||||
-rwxr-xr-x | integration-tests/test-ebics.py | 10 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt | 125 | ||||
-rw-r--r-- | nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt | 73 |
4 files changed, 111 insertions, 99 deletions
diff --git a/integration-tests/test-ebics-highlevel.py b/integration-tests/test-ebics-highlevel.py index 4961f820..da4306c9 100755 --- a/integration-tests/test-ebics-highlevel.py +++ b/integration-tests/test-ebics-highlevel.py @@ -152,7 +152,7 @@ assertResponse( # 2.c, fetch bank account information assertResponse( post( - "http://localhost:5001/bank-connections/my-ebics/import-accounts", + "http://localhost:5001/bank-connections/my-ebics/ebics/import-accounts", json=dict(), headers=dict(Authorization=USER_AUTHORIZATION_HEADER), ) diff --git a/integration-tests/test-ebics.py b/integration-tests/test-ebics.py index c81a1cee..5b5c2563 100755 --- a/integration-tests/test-ebics.py +++ b/integration-tests/test-ebics.py @@ -144,7 +144,7 @@ print("sending ini & hia") # 2.a, upload keys to the bank (INI & HIA) assertResponse( post( - "http://localhost:5001/bank-connections/my-ebics/send-ini", + "http://localhost:5001/bank-connections/my-ebics/ebics/send-ini", json=dict(), headers=dict(Authorization=USER_AUTHORIZATION_HEADER), ) @@ -152,7 +152,7 @@ assertResponse( assertResponse( post( - "http://localhost:5001/bank-connections/my-ebics/send-hia", + "http://localhost:5001/bank-connections/my-ebics/ebics/send-hia", json=dict(), headers=dict(Authorization=USER_AUTHORIZATION_HEADER), ) @@ -161,7 +161,7 @@ assertResponse( # 2.b, download keys from the bank (HPB) assertResponse( post( - "http://localhost:5001/bank-connections/my-ebics/send-hpb", + "http://localhost:5001/bank-connections/my-ebics/ebics/send-hpb", json=dict(), headers=dict(Authorization=USER_AUTHORIZATION_HEADER), ) @@ -170,7 +170,7 @@ assertResponse( # Test download transaction (TSD, LibEuFin-specific test order type) assertResponse( post( - "http://localhost:5001/bank-connections/my-ebics/download/tsd", + "http://localhost:5001/bank-connections/my-ebics/ebics/download/tsd", headers=dict(Authorization=USER_AUTHORIZATION_HEADER), ) ) @@ -178,7 +178,7 @@ assertResponse( # 2.c, fetch bank account information assertResponse( post( - "http://localhost:5001/bank-connections/my-ebics/import-accounts", + "http://localhost:5001/bank-connections/my-ebics/ebics/import-accounts", json=dict(), headers=dict(Authorization=USER_AUTHORIZATION_HEADER), ) diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt index 35176631..48546b20 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsNexus.kt @@ -325,6 +325,45 @@ private fun getEbicsSubscriberDetails(bankConnectionId: String): EbicsClientSubs return getEbicsSubscriberDetailsInternal(subscriber) } +suspend fun ebicsFetchAccounts(connId: String, client: HttpClient) { + val subscriberDetails = transaction { + getEbicsSubscriberDetails(connId) + } + val response = doEbicsDownloadTransaction( + client, subscriberDetails, "HTD", EbicsStandardOrderParams() + ) + when (response) { + is EbicsDownloadBankErrorResult -> { + throw NexusError( + HttpStatusCode.BadGateway, + response.returnCode.errorCode + ) + } + is EbicsDownloadSuccessResult -> { + val payload = XMLUtil.convertStringToJaxb<HTDResponseOrderData>( + response.orderData.toString(Charsets.UTF_8) + ) + transaction { + payload.value.partnerInfo.accountInfoList?.forEach { + OfferedBankAccountEntity.new(id = it.id) { + accountHolder = it.accountHolder ?: "NOT-GIVEN" + iban = it.accountNumberList?.filterIsInstance<EbicsTypes.GeneralAccountNumber>() + ?.find { it.international }?.value + ?: throw NexusError(HttpStatusCode.NotFound, reason = "bank gave no IBAN") + bankCode = it.bankCodeList?.filterIsInstance<EbicsTypes.GeneralBankCode>() + ?.find { it.international }?.value + ?: throw NexusError( + HttpStatusCode.NotFound, + reason = "bank gave no BIC" + ) + bankConnection = requireBankConnectionInternal(connId) + } + } + } + } + } +} + fun Route.ebicsBankProtocolRoutes(client: HttpClient) { post("test-host") { val r = call.receiveJson<EbicsHostTestRequest>() @@ -392,92 +431,6 @@ fun Route.ebicsBankConnectionRoutes(client: HttpClient) { } call.respond(object {}) } - // persists raw / to-be-imported bank accounts - post("/accounts/fetch") { - val res = transaction { - authenticateRequest(call.request) - val conn = requireBankConnection(call, "connid") - if (conn.type != "ebics") { - throw NexusError(HttpStatusCode.BadRequest, "bank connection is not of type 'ebics'") - } - object { - val subscriberDetails = getEbicsSubscriberDetails(conn.id.value) - val connid = conn.id.value - } - } - val response = doEbicsDownloadTransaction( - client, res.subscriberDetails, "HTD", EbicsStandardOrderParams() - ) - when (response) { - is EbicsDownloadBankErrorResult -> { - throw NexusError( - HttpStatusCode.BadGateway, - response.returnCode.errorCode - ) - } - is EbicsDownloadSuccessResult -> { - val payload = XMLUtil.convertStringToJaxb<HTDResponseOrderData>( - response.orderData.toString(Charsets.UTF_8) - ) - transaction { - val conn = requireBankConnection(call, "connid") - payload.value.partnerInfo.accountInfoList?.forEach { - OfferedBankAccountEntity.new(id = it.id) { - accountHolder = it.accountHolder ?: "NOT-GIVEN" - iban = it.accountNumberList?.filterIsInstance<EbicsTypes.GeneralAccountNumber>() - ?.find { it.international }?.value - ?: throw NexusError(HttpStatusCode.NotFound, reason = "bank gave no IBAN") - bankCode = it.bankCodeList?.filterIsInstance<EbicsTypes.GeneralBankCode>() - ?.find { it.international }?.value - ?: throw NexusError( - HttpStatusCode.NotFound, - reason = "bank gave no BIC" - ) - bankConnection = conn - } - } - } - } - } - call.respond(object {}) - } - - // show only non imported accounts. - get("/accounts") { - val ret = BankAccounts() - transaction { - val conn = requireBankConnection(call, "connid") - OfferedBankAccountEntity.find { - OfferedBankAccountsTable.bankConnection eq conn.id.value and not(OfferedBankAccountsTable.imported) - }.forEach { - ret.accounts.add( - BankAccount( - iban = it.iban, bic = it.bankCode, holder = it.accountHolder, account = it.id.value - ) - ) - } - } - call.respond(ret) - } - - post("/accounts/import") { - val body = call.receive<ImportBankAccount>() - transaction { - val conn = requireBankConnection(call, "connid") - val account = OfferedBankAccountEntity.findById(body.accountId) ?: throw NexusError( - HttpStatusCode.NotFound, "Could not found raw bank account '${body.accountId}'" - ) - NexusBankAccountEntity.new(body.localName) { - iban = account.iban - bankCode = account.bankCode - defaultBankConnection = conn - highestSeenBankMessageId = 0 - accountHolder = account.accountHolder - } - account.imported = true - } - call.respond(object {}) - } /** * Directly import accounts. Used for testing. diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt index 3186d75e..f0514503 100644 --- a/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt +++ b/nexus/src/main/kotlin/tech/libeufin/nexus/server/NexusServer.kt @@ -49,6 +49,7 @@ import io.ktor.utils.io.ByteReadChannel import io.ktor.utils.io.jvm.javaio.toByteReadChannel import io.ktor.utils.io.jvm.javaio.toInputStream import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.not import org.jetbrains.exposed.sql.transactions.transaction import org.slf4j.event.Level import tech.libeufin.nexus.* @@ -58,6 +59,8 @@ import tech.libeufin.nexus.bankaccount.getPaymentInitiation import tech.libeufin.nexus.bankaccount.submitPaymentInitiation import tech.libeufin.nexus.ebics.* import tech.libeufin.util.* +import tech.libeufin.util.ebics_h004.EbicsTypes +import tech.libeufin.util.ebics_h004.HTDResponseOrderData import tech.libeufin.util.logger import java.lang.IllegalArgumentException import java.net.URLEncoder @@ -178,17 +181,20 @@ fun createLoopbackBankConnection(bankConnectionName: String, user: NexusUserEnti } } +fun requireBankConnectionInternal(connId: String): NexusBankConnectionEntity { + val conn = transaction { NexusBankConnectionEntity.findById(connId) } + if (conn == null) { + throw NexusError(HttpStatusCode.NotFound, "bank connection '$connId' not found") + } + return conn +} fun requireBankConnection(call: ApplicationCall, parameterKey: String): NexusBankConnectionEntity { val name = call.parameters[parameterKey] if (name == null) { throw NexusError(HttpStatusCode.InternalServerError, "no parameter for bank connection") } - val conn = transaction { NexusBankConnectionEntity.findById(name) } - if (conn == null) { - throw NexusError(HttpStatusCode.NotFound, "bank connection '$name' not found") - } - return conn + return requireBankConnectionInternal(name) } @@ -700,7 +706,7 @@ fun serverMain(dbName: String, host: String) { val pdfBytes = getEbicsKeyLetterPdf(conn) call.respondBytes(pdfBytes, ContentType("application", "pdf")) } - else -> throw NexusError(HttpStatusCode.NotImplemented, "keyletter not supporte dfor ${conn.type}") + else -> throw NexusError(HttpStatusCode.NotImplemented, "keyletter not supported for ${conn.type}") } } @@ -766,7 +772,60 @@ fun serverMain(dbName: String, host: String) { } route("/bank-connections/{connid}") { - ebicsBankConnectionRoutes(client) + // only ebics specific tasks under this part. + route("/ebics") { + ebicsBankConnectionRoutes(client) + } + post("/accounts/fetch") { + val conn = transaction { + authenticateRequest(call.request) + requireBankConnection(call, "connid") + } + when(conn.type) { + "ebics" -> { + ebicsFetchAccounts(conn.id.value, client) + } + else -> throw NexusError(HttpStatusCode.NotImplemented, "connection not supported for ${conn.type}") + } + call.respond(object {}) + } + + // show all the offered accounts (both imported and non) + get("/accounts") { + val ret = BankAccounts() + transaction { + val conn = requireBankConnection(call, "connid") + OfferedBankAccountEntity.find { + OfferedBankAccountsTable.bankConnection eq conn.id.value + }.forEach { + ret.accounts.add( + BankAccount( + iban = it.iban, bic = it.bankCode, holder = it.accountHolder, account = it.id.value + ) + ) + } + } + call.respond(ret) + } + // import one account into libeufin. + post("/accounts/import") { + val body = call.receive<ImportBankAccount>() + transaction { + val conn = requireBankConnection(call, "connid") + val account = OfferedBankAccountEntity.findById(body.accountId) ?: throw NexusError( + HttpStatusCode.NotFound, "Could not found raw bank account '${body.accountId}'" + ) + NexusBankAccountEntity.new(body.localName) { + iban = account.iban + bankCode = account.bankCode + defaultBankConnection = conn + highestSeenBankMessageId = 0 + accountHolder = account.accountHolder + } + account.imported = true + } + call.respond(object {}) + } } route("/facades/{fcid}/taler") { |