commit fa2705f9f449a0d94a9c92ed101c3e597f673bac
parent 14cabeb098524400fa64d404384a589b95509c96
Author: Marcello Stanisci <stanisci.m@gmail.com>
Date: Wed, 23 Oct 2019 13:48:56 +0200
Generating INI response.
Diffstat:
7 files changed, 194 insertions(+), 118 deletions(-)
diff --git a/sandbox/src/main/kotlin/DB.kt b/sandbox/src/main/kotlin/DB.kt
@@ -11,7 +11,6 @@ const val EBICS_SYSTEM_ID_MAX_LENGTH = 10
const val PUBLIC_KEY_MAX_MODULUS_LENGTH = 256 // FIXME review this value!
const val PUBLIC_KEY_MAX_EXPONENT_LENGTH = 256 // FIXME review this value!
const val PRIV_KEY_MAX_LENGTH = 512 // FIXME review this value!
-const val SQL_ENUM_SUBSCRIBER_STATES = "ENUM('NEW', 'PARTIALLI_INITIALIZED_INI', 'PARTIALLY_INITIALIZED_HIA', 'INITIALIZED', 'READY')"
/**
* All the states to give a subscriber.
@@ -23,12 +22,12 @@ enum class SubscriberStates {
NEW,
/**
- * Only INI electronic message was succesfully sent.
+ * Only INI electronic message was successfully sent.
*/
PARTIALLY_INITIALIZED_INI,
/**
- * Only HIA electronic message was succesfully sent.
+ * Only HIA electronic message was successfully sent.
*/
PARTIALLY_INITIALIZED_HIA,
@@ -154,11 +153,7 @@ class EbicsSystem(id: EntityID<Int>) : IntEntity(id) {
object EbicsPublicKeys: IntIdTable() {
val modulus = binary("modulus", PUBLIC_KEY_MAX_MODULUS_LENGTH)
val exponent = binary("exponent", PUBLIC_KEY_MAX_EXPONENT_LENGTH)
- val state = customEnumeration(
- "state",
- "ENUM('MISSING', 'NEW', 'RELEASED')",
- {KeyStates.values()[it as Int]},
- {it.name})
+ val state = enumeration("state", KeyStates::class)
}
@@ -186,11 +181,7 @@ object EbicsSubscribers: IntIdTable() {
val encryptionKey = reference("encryptionKey", EbicsPublicKeys).nullable()
val authorizationKey = reference("authorizationKey", EbicsPublicKeys).nullable()
- val state = customEnumeration(
- "state",
- SQL_ENUM_SUBSCRIBER_STATES,
- {SubscriberStates.values()[it as Int]},
- {it.name})
+ val state = enumeration("state", SubscriberStates::class)
}
class EbicsSubscriber(id: EntityID<Int>) : IntEntity(id) {
diff --git a/sandbox/src/main/kotlin/EbicsResponse.kt b/sandbox/src/main/kotlin/EbicsResponse.kt
@@ -0,0 +1,44 @@
+package tech.libeufin.sandbox
+
+import tech.libeufin.messages.ebics.response.EbicsResponse
+import tech.libeufin.messages.ebics.response.ObjectFactory
+import javax.xml.bind.JAXBElement
+import javax.xml.namespace.QName
+
+/**
+ * Convenience wrapper around the main JAXB value.
+ *
+ * @param returnCode return code
+ * @param reportText EBICS-compliant error text token, e.g. "[EBICS_OK]" (mandatory brackets!)
+ * @param description short description about the response, e.g. "invalid signature".
+ */
+class EbicsResponse(
+ returnCode: String,
+ reportText: String
+) {
+
+ /**
+ * For now, the sandbox returns _only_ technical return codes,
+ * namely those that are _not_ related with business orders. Therefore,
+ * the relevant fields to fill are "ebicsResponse/header/mutable/report{Text,Code}".
+ *
+ * Once business return code will be returned, then the following fields will
+ * also have to be filled out: "ebicsResponse/body/report{Text,Code}".
+ */
+ private val value = {
+ val of = ObjectFactory()
+ val tmp = of.createEbicsResponse()
+ tmp.header = of.createEbicsResponseHeader()
+ tmp.header.mutable = of.createResponseMutableHeaderType()
+ tmp.header.mutable.reportText = reportText
+ tmp.header.mutable.returnCode = returnCode
+ tmp
+ }()
+
+ fun get(): JAXBElement<EbicsResponse> {
+ return JAXBElement(
+ QName("urn:org:ebics:H004", "ebicsResponse"),
+ EbicsResponse::class.java,
+ value)
+ }
+}
diff --git a/sandbox/src/main/kotlin/KeyManagementResponse.kt b/sandbox/src/main/kotlin/KeyManagementResponse.kt
@@ -6,10 +6,10 @@ import javax.xml.bind.JAXBElement
import javax.xml.namespace.QName
class KeyManagementResponse(
- version: String,
- revision: Int,
+ version: String = "H004",
+ revision: Int = 1,
returnCode: String,
- orderId: String,
+ orderId: String = "<automatically generated by the bank>",
reportText: String
) {
diff --git a/sandbox/src/main/kotlin/Main.kt b/sandbox/src/main/kotlin/Main.kt
@@ -43,31 +43,87 @@ import org.w3c.dom.Document
import org.w3c.dom.Element
import tech.libeufin.messages.ebics.keyrequest.EbicsUnsecuredRequest
import tech.libeufin.messages.ebics.keyrequest.SignaturePubKeyOrderDataType
-import tech.libeufin.messages.ebics.keyrequest.UnsecuredReqOrderDetailsType
-import tech.libeufin.messages.ebics.keyresponse.EbicsKeyManagementResponse
import java.math.BigInteger
import java.nio.charset.StandardCharsets.US_ASCII
import java.text.DateFormat
-import java.util.*
-import java.util.zip.GZIPInputStream
-import javax.xml.bind.JAXBElement
-import java.nio.charset.StandardCharsets.UTF_8
-import java.security.InvalidKeyException
import java.security.KeyFactory
import java.security.PublicKey
-import java.security.interfaces.RSAPublicKey
import java.security.spec.RSAPublicKeySpec
-import java.util.zip.Inflater
import java.util.zip.InflaterInputStream
-import javax.xml.bind.JAXB
-
-
val logger = LoggerFactory.getLogger("tech.libeufin.sandbox")
val xmlProcess = XML()
val getEbicsHostId = {"LIBEUFIN-SANDBOX"}
+object UserUnknownHelper {
+
+ fun getCode(): String {
+ return "091003"
+ }
+
+ /**
+ * @param description: will be concatenated to the error token word.
+ * @return full error string: token + description
+ */
+ fun getMessage(description: String = ""): String {
+ return "[EBICS_UNKNOWN_USER] $description"
+ }
+}
+
+
+object InvalidHostIdHelper {
+
+ fun getCode(): String {
+ return "091011"
+ }
+
+ /**
+ * @param description: will be concatenated to the error token word.
+ * @return full error string: token + description
+ */
+ fun getMessage(description: String = ""): String {
+ return "[EBICS_INVALID_HOST_ID] $description"
+ }
+}
+
+object InvalidXmlHelper {
+
+ fun getCode(): String {
+ return "091010"
+ }
+
+ /**
+ * @param description: will be concatenated to the error token word.
+ * @return full error string: token + description
+ */
+ fun getMessage(description: String = ""): String {
+ return "[EBICS_INVALID_XML] $description"
+ }
+}
+
+object InternalErrorHelper {
+
+ fun getCode(): String {
+ return "061099"
+ }
+
+ fun getMessage(description: String = ""): String {
+ return "[EBICS_INTERNAL_ERROR] $description"
+ }
+}
+
+object OkHelper {
+
+ fun getCode(): String {
+ return "000000"
+ }
+
+ fun getMessage(description: String = ""): String {
+ return "[EBICS_OK] $description"
+ }
+}
+
/**
* Sometimes, JAXB is not able to figure out to which type
* a certain XML node should be bound to. This happens when
@@ -215,21 +271,17 @@ private suspend fun ApplicationCall.ebicsweb() {
val body: String = receiveText()
val bodyDocument: Document? = xmlProcess.parseStringIntoDom(body)
- if (bodyDocument == null) {
- respondText(
- contentType = ContentType.Application.Xml,
- status = HttpStatusCode.BadRequest
- ) { "Bad request / Could not parse the body" }
- return
- }
+ if (bodyDocument == null || (!xmlProcess.validateFromDom(bodyDocument))) {
+ var response = EbicsResponse(
+ returnCode = InvalidXmlHelper.getCode(),
+ reportText = InvalidXmlHelper.getMessage()
+ )
- if (!xmlProcess.validateFromDom(bodyDocument)) {
- logger.error("Invalid request received")
respondText(
contentType = ContentType.Application.Xml,
status = HttpStatusCode.BadRequest
- ) { "Bad request / invalid document" }
+ ) { xmlProcess.convertJaxbToString(response.get())!! }
return
}
@@ -248,10 +300,16 @@ private suspend fun ApplicationCall.ebicsweb() {
)
if (bodyJaxb.value.header.static.hostID != getEbicsHostId()) {
- respond(
- HttpStatusCode.NotFound,
- SandboxError("Unknown HostID specified")
- )
+
+ // return INI response!
+ val response = KeyManagementResponse(
+ returnCode = InvalidHostIdHelper.getCode(),
+ reportText = InvalidHostIdHelper.getMessage( ))
+
+ respondText(
+ contentType = ContentType.Application.Xml,
+ status = HttpStatusCode.NotFound
+ ) { xmlProcess.convertJaxbToString(response.get())!! }
return
}
val ebicsUserID = transaction {
@@ -259,10 +317,16 @@ private suspend fun ApplicationCall.ebicsweb() {
}
if (ebicsUserID == null) {
- respond(
- HttpStatusCode.NotFound,
- SandboxError("Ebics UserID not found")
+ val response = KeyManagementResponse(
+ returnCode = UserUnknownHelper.getCode(),
+ reportText = UserUnknownHelper.getMessage()
)
+
+ respondText(
+ contentType = ContentType.Application.Xml,
+ status = HttpStatusCode.NotFound
+ ) { xmlProcess.convertJaxbToString(response.get())!! }
+
return
}
@@ -285,11 +349,16 @@ private suspend fun ApplicationCall.ebicsweb() {
* shall the schema be patched to avoid having this if-block here?
*/
if (zkey.isEmpty()) {
- logger.error("0-length key element given, invalid request")
+ logger.info("0-length key element given, invalid request")
+ var response = KeyManagementResponse(
+ returnCode = InvalidXmlHelper.getCode(),
+ reportText = InvalidXmlHelper.getMessage("Key field was empty")
+ )
+
respondText(
contentType = ContentType.Text.Plain,
status = HttpStatusCode.BadRequest
- ) { "Bad request / invalid document" }
+ ) { xmlProcess.convertJaxbToString(response.get())!! }
return
}
@@ -320,10 +389,17 @@ private suspend fun ApplicationCall.ebicsweb() {
} catch (e: Exception) {
logger.info("User gave bad key, not storing it")
e.printStackTrace()
- respond(
- HttpStatusCode.BadRequest,
- SandboxError("Bad public key given")
+ val response = KeyManagementResponse(
+ returnCode = InvalidXmlHelper.getCode(),
+ reportText = InvalidXmlHelper.getMessage("Invalid key given")
)
+
+
+ respondText(
+ contentType = ContentType.Application.Xml,
+ status = HttpStatusCode.BadRequest
+ ) { xmlProcess.convertJaxbToString(response.get())!! }
+
return
}
@@ -340,10 +416,17 @@ private suspend fun ApplicationCall.ebicsweb() {
* row is also (via a helper function) added into the EbicsSubscribers table.
*/
if (ebicsSubscriber == null) {
- respond(
- HttpStatusCode.InternalServerError,
- SandboxError("Internal error, please contact customer service")
+
+ val response = KeyManagementResponse(
+ returnCode = InternalErrorHelper.getCode(),
+ reportText = InternalErrorHelper.getMessage()
)
+
+ respondText(
+ status = HttpStatusCode.InternalServerError,
+ contentType = ContentType.Application.Xml
+ ) { InternalErrorHelper.getMessage() }
+
return
}
@@ -354,33 +437,38 @@ private suspend fun ApplicationCall.ebicsweb() {
exponent = keyObject.value.signaturePubKeyInfo.pubKeyValue.rsaKeyValue.exponent
state = KeyStates.NEW
}
+
+ ebicsSubscriber.state = SubscriberStates.PARTIALLY_INITIALIZED_INI
}
- logger.debug("Signature key inserted in database.")
+ logger.info(
+ "Signature key inserted in database _and_ subscriber state changed accordingly"
+ )
// return INI response!
val response = KeyManagementResponse(
- "H004",
- 1,
- "000000",
- "MOCK-ID",
- "[EBICS_OK] OK")
+ returnCode = OkHelper.getCode(),
+ reportText = OkHelper.getMessage()
+ )
respondText(
contentType = ContentType.Application.Xml,
- status = HttpStatusCode.OK) {
- xmlProcess.convertJaxbToString(response.get()).toString()
- }
+ status = HttpStatusCode.OK
+ ) { xmlProcess.convertJaxbToString(response.get())!! }
return
}
- }
- respond(
- HttpStatusCode.NotImplemented,
- SandboxError("Not implemented")
- )
- return
+ "HIA" -> {
+
+ respond(
+ HttpStatusCode.NotImplemented,
+ SandboxError("Not implemented")
+ )
+ return
+
+ }
+ }
}
"ebicsHEVRequest" -> {
diff --git a/sandbox/src/main/kotlin/Response.kt b/sandbox/src/main/kotlin/Response.kt
@@ -1,44 +0,0 @@
-package tech.libeufin.sandbox
-
-import tech.libeufin.messages.ebics.response.EbicsResponse
-import tech.libeufin.messages.ebics.response.ObjectFactory
-import javax.xml.bind.JAXBElement
-import javax.xml.namespace.QName
-
-/**
- * Convenience wrapper around the main JAXB value.
- *
- * @param returnCode return code
- * @param reportText EBICS-compliant error text token, e.g. "[EBICS_OK]" (mandatory brackets!)
- * @param description short description about the response, e.g. "invalid signature".
- */
-class Response(
- returnCode: String,
- reportText: String,
- description: String) {
-
- /**
- * For now, the sandbox returns _only_ technical return codes,
- * namely those that are _not_ related with business orders. Therefore,
- * the relevant fields to fill are "ebicsResponse/header/mutable/report{Text,Code}".
- *
- * Once business return code will be returned, then the following fields will
- * also have to be filled out: "ebicsResponse/body/report{Text,Code}".
- */
- private val value = {
- val of = ObjectFactory()
- val tmp = of.createEbicsResponse()
- tmp.header = of.createEbicsResponseHeader()
- tmp.header.mutable = of.createResponseMutableHeaderType()
- tmp.header.mutable.reportText = "$reportText $description"
- tmp.header.mutable.returnCode = returnCode
- tmp
- }()
-
- fun get(): JAXBElement<EbicsResponse> {
- return JAXBElement(
- QName("urn:org:ebics:H004", "ebicsResponse"),
- EbicsResponse::class.java,
- value)
- }
-}
diff --git a/sandbox/src/test/kotlin/ResponseTest.kt b/sandbox/src/test/kotlin/ResponseTest.kt
@@ -1,9 +1,6 @@
package tech.libeufin.sandbox
-import org.junit.Assert
import org.junit.Test
-import org.junit.Assert.*
-import org.junit.Before
class ResponseTest {
@@ -11,8 +8,8 @@ class ResponseTest {
@Test
fun loadResponse() {
- val response = Response(
- "0000",
+ val response = EbicsResponse(
+ "0000",
"[EBICS_OK]",
"All is OK."
)
diff --git a/sandbox/src/test/resources/ebics_ini_request_sample.xml b/sandbox/src/test/resources/ebics_ini_request_sample.xml
@@ -4,7 +4,7 @@
<static>
<HostID>LIBEUFIN-SANDBOX</HostID>
<PartnerID>flokid</PartnerID>
- <UserID>flouid</UserID>
+ <UserID>u1</UserID>
<!--
Such a not allowed renaming like this fixes the import of DOM into JAXB.