libeufin

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

commit b7a3a024d388d4cc7b368f9d15a0289097a815a1
parent c70d0ae3f357898a1547881a09489563830722f9
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date:   Thu,  7 Nov 2019 20:09:58 +0100

send HPB to bank, response to be parsed.

Diffstat:
Mnexus/src/main/kotlin/Main.kt | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt | 2+-
2 files changed, 87 insertions(+), 16 deletions(-)

diff --git a/nexus/src/main/kotlin/Main.kt b/nexus/src/main/kotlin/Main.kt @@ -23,16 +23,12 @@ import io.ktor.application.ApplicationCallPipeline import io.ktor.application.call import io.ktor.application.install import io.ktor.client.* -import io.ktor.client.features.ServerResponseException -import io.ktor.client.request.get import io.ktor.client.request.post import io.ktor.features.ContentNegotiation import io.ktor.features.StatusPages import io.ktor.gson.gson import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode -import io.ktor.http.URLBuilder -import io.ktor.http.takeFrom import io.ktor.request.receive import io.ktor.request.uri import io.ktor.response.respond @@ -43,19 +39,23 @@ import io.ktor.routing.routing import io.ktor.server.engine.embeddedServer import io.ktor.server.netty.Netty import org.apache.xml.security.binding.xmldsig.RSAKeyValueType +import org.apache.xml.security.binding.xmldsig.SignatureType import org.jetbrains.exposed.sql.transactions.transaction import org.slf4j.LoggerFactory import tech.libeufin.sandbox.* -import tech.libeufin.schema.ebics_h004.EbicsKeyManagementResponse -import tech.libeufin.schema.ebics_h004.EbicsTypes -import tech.libeufin.schema.ebics_h004.EbicsUnsecuredRequest -import tech.libeufin.schema.ebics_h004.HIARequestOrderData +import tech.libeufin.schema.ebics_h004.* import tech.libeufin.schema.ebics_s001.PubKeyValueType import tech.libeufin.schema.ebics_s001.SignaturePubKeyInfoType import tech.libeufin.schema.ebics_s001.SignaturePubKeyOrderData import java.text.DateFormat import javax.sql.rowset.serial.SerialBlob import javax.xml.bind.JAXBElement +import org.w3c.dom.Document +import java.security.SecureRandom +import java.util.* +import javax.xml.datatype.DatatypeFactory +import javax.xml.datatype.XMLGregorianCalendar + fun testData() { @@ -90,13 +90,14 @@ fun expectId(param: String?) : Int { * @return null when the bank could not be reached, otherwise returns the * response already converted in JAXB. */ -suspend inline fun <reified S, reified T>HttpClient.postToBank(url: String, body: T) : JAXBElement<S>? { +// suspend inline fun <reified S, reified T>HttpClient.postToBank(url: String, body: JAXBElement<T>) : JAXBElement<S>? { +suspend inline fun <reified S>HttpClient.postToBank(url: String, body: String): JAXBElement<S>? { val response = try { this.post<String>( urlString = url, block = { - this.body = XMLUtil.convertJaxbToString(body) + this.body = body } ) } catch (e: Exception) { @@ -108,11 +109,37 @@ suspend inline fun <reified S, reified T>HttpClient.postToBank(url: String, body return XMLUtil.convertStringToJaxb(response) } +// takes JAXB +suspend inline fun <reified T, reified S>HttpClient.postToBank(url: String, body: T): JAXBElement<S>? { + return this.postToBank<S>(url, XMLUtil.convertJaxbToString(body)) +} + +// takes DOM +suspend inline fun <reified S>HttpClient.postToBank(url: String, body: Document): JAXBElement<S>? { + return this.postToBank<S>(url, XMLUtil.convertDomToString(body)) +} + +/** + * @param size in bits + */ +fun getNonce(size: Int): ByteArray { + val sr = SecureRandom() + val ret = ByteArray(size / 8) + sr.nextBytes(ret) + return ret +} + +fun getGregorianDate(): XMLGregorianCalendar { + val gregorianCalendar = GregorianCalendar() + val datatypeFactory = DatatypeFactory.newInstance() + return datatypeFactory.newXMLGregorianCalendar(gregorianCalendar) +} + data class NotAnIdError(val statusCode: HttpStatusCode) : Exception("String ID not convertible in number") data class SubscriberNotFoundError(val statusCode: HttpStatusCode) : Exception("Subscriber not found in database") data class UnreachableBankError(val statusCode: HttpStatusCode) : Exception("Could not reach the bank") -data class EbicsError(val codeError: String) : Exception("Bank did not accepted EBICS request, error is: " + - "${codeError}") +data class EbicsError(val codeError: String) : Exception("Bank did not accepted EBICS request, error is: " + codeError +) fun main() { @@ -272,7 +299,7 @@ fun main() { subscriber.ebicsURL } - val responseJaxb = client.postToBank<EbicsKeyManagementResponse, EbicsUnsecuredRequest>( + val responseJaxb = client.postToBank<EbicsUnsecuredRequest, EbicsKeyManagementResponse>( url, iniRequest ) ?: throw UnreachableBankError(HttpStatusCode.InternalServerError) @@ -288,6 +315,51 @@ fun main() { return@post } + post("/ebics/subscribers/{id}/sync") { + // fetch sub's EBICS URL, done + // prepare message, done + // send it out, done + // _parse_ response! + // respond to client + val id = expectId(call.parameters["id"]) + val (url, body) = transaction { + val subscriber = EbicsSubscriberEntity.findById(id) ?: throw SubscriberNotFoundError(HttpStatusCode.NotFound) + val hpbRequest = EbicsNpkdRequest().apply { + version = "H004" + revision = 1 + header = EbicsNpkdRequest.Header().apply { + authenticate = true + mutable = EbicsNpkdRequest.EmptyMutableHeader() + static = EbicsNpkdRequest.StaticHeaderType().apply { + hostID = subscriber.hostID + partnerID = subscriber.partnerID + userID = subscriber.userID + securityMedium = "0000" + orderDetails = EbicsNpkdRequest.OrderDetails() + orderDetails.orderType = "HPB" + orderDetails.orderAttribute = "DZHNN" + nonce = getNonce(128) + timestamp = getGregorianDate() + } + } + body = EbicsNpkdRequest.EmptyBody() + authSignature = SignatureType() + } + val hpbText = XMLUtil.convertJaxbToString(hpbRequest) + val hpbDoc = XMLUtil.parseStringIntoDom(hpbText) + XMLUtil.signEbicsDocument( + hpbDoc, + CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray()) + ) + Pair(subscriber.ebicsURL, hpbDoc) + } + + val response = client.postToBank<EbicsKeyManagementResponse>(url, body) + + call.respond(HttpStatusCode.NotImplemented, NexusError("work in progress")) + return@post + } + post("/ebics/subscribers/{id}/sendHia") { val id = expectId(call.parameters["id"]) // caught above @@ -348,11 +420,10 @@ fun main() { } } } - subscriber.ebicsURL } - val responseJaxb = client.postToBank<EbicsKeyManagementResponse, EbicsUnsecuredRequest>( + val responseJaxb = client.postToBank<EbicsUnsecuredRequest, EbicsKeyManagementResponse>( url, hiaRequest ) ?: throw UnreachableBankError(HttpStatusCode.InternalServerError) diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/XMLUtil.kt @@ -259,7 +259,7 @@ class XMLUtil private constructor() { } /** - * Convert a DOM document - of a XML document - to the JAXB representation. + * Convert a DOM document to the JAXB representation. * * @param finalType class type of the output * @param document the document to convert into JAXB.