commit 82133eede8a316c975231f984bb34d13f08914c7
parent 17aabee238c639435cc0f507095e05691ef26eaf
Author: Florian Dold <florian.dold@gmail.com>
Date: Wed, 3 Jun 2020 13:17:50 +0530
upgrade dependencies, use ExposedBlob instead of SerialBlob
Diffstat:
13 files changed, 179 insertions(+), 163 deletions(-)
diff --git a/nexus/build.gradle b/nexus/build.gradle
@@ -54,11 +54,10 @@ compileTestKotlin {
def ktor_version = "1.3.2"
-
+def exposed_version = "0.24.1"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
- implementation "org.jetbrains.exposed:exposed:0.17.6"
implementation "ch.qos.logback:logback-classic:1.2.3"
implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'
implementation "javax.xml.bind:jaxb-api:2.3.0"
@@ -74,6 +73,12 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
+ // Exposed, an SQL library
+ implementation "org.jetbrains.exposed:exposed-core:$exposed_version"
+ implementation "org.jetbrains.exposed:exposed-dao:$exposed_version"
+ implementation "org.jetbrains.exposed:exposed-jdbc:$exposed_version"
+
+ // Ktor, an HTTP client and server library
implementation "io.ktor:ktor-server-core:$ktor_version"
implementation "io.ktor:ktor-client-apache:$ktor_version"
implementation "io.ktor:ktor-server-netty:$ktor_version"
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt
@@ -2,14 +2,16 @@ package tech.libeufin.nexus
import io.ktor.http.HttpStatusCode
import org.jetbrains.exposed.dao.*
+import org.jetbrains.exposed.dao.id.LongIdTable
+import org.jetbrains.exposed.dao.id.EntityID
+import org.jetbrains.exposed.dao.id.IdTable
+import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.StdOutSqlLogger
import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
-import tech.libeufin.nexus.NexusBankConnectionsTable.entityId
-import tech.libeufin.nexus.NexusBankConnectionsTable.primaryKey
import tech.libeufin.util.EbicsInitState
import tech.libeufin.util.amount
import java.sql.Connection
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt
@@ -5,6 +5,7 @@ import io.ktor.http.HttpStatusCode
import io.ktor.request.ApplicationRequest
import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.and
+import org.jetbrains.exposed.sql.statements.api.ExposedBlob
import org.jetbrains.exposed.sql.transactions.transaction
import org.w3c.dom.Document
import tech.libeufin.util.*
@@ -16,7 +17,6 @@ import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.*
-import javax.sql.rowset.serial.SerialBlob
fun isProduction(): Boolean {
return System.getenv("NEXUS_PRODUCTION") != null
@@ -64,13 +64,13 @@ fun getEbicsSubscriberDetailsInternal(subscriber: EbicsSubscriberEntity): EbicsC
var bankAuthPubValue: RSAPublicKey? = null
if (subscriber.bankAuthenticationPublicKey != null) {
bankAuthPubValue = CryptoUtil.loadRsaPublicKey(
- subscriber.bankAuthenticationPublicKey?.toByteArray()!!
+ subscriber.bankAuthenticationPublicKey?.bytes!!
)
}
var bankEncPubValue: RSAPublicKey? = null
if (subscriber.bankEncryptionPublicKey != null) {
bankEncPubValue = CryptoUtil.loadRsaPublicKey(
- subscriber.bankEncryptionPublicKey?.toByteArray()!!
+ subscriber.bankEncryptionPublicKey?.bytes!!
)
}
return EbicsClientSubscriberDetails(
@@ -82,9 +82,9 @@ fun getEbicsSubscriberDetailsInternal(subscriber: EbicsSubscriberEntity): EbicsC
userId = subscriber.userID,
partnerId = subscriber.partnerID,
- customerSignPriv = CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.toByteArray()),
- customerAuthPriv = CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.toByteArray()),
- customerEncPriv = CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.toByteArray()),
+ customerSignPriv = CryptoUtil.loadRsaPrivateKey(subscriber.signaturePrivateKey.bytes),
+ customerAuthPriv = CryptoUtil.loadRsaPrivateKey(subscriber.authenticationPrivateKey.bytes),
+ customerEncPriv = CryptoUtil.loadRsaPrivateKey(subscriber.encryptionPrivateKey.bytes),
ebicsIniState = subscriber.ebicsIniState,
ebicsHiaState = subscriber.ebicsHiaState
)
@@ -157,7 +157,7 @@ fun ingestBankMessagesIntoAccount(
(NexusBankMessagesTable.id greater acct.highestSeenBankMessageId)
}.orderBy(Pair(NexusBankMessagesTable.id, SortOrder.ASC)).forEach {
// FIXME: check if it's CAMT first!
- val doc = XMLUtil.parseStringIntoDom(it.message.toByteArray().toString(Charsets.UTF_8))
+ val doc = XMLUtil.parseStringIntoDom(it.message.bytes.toString(Charsets.UTF_8))
processCamtMessage(bankAccountId, doc)
lastId = it.id.value
}
@@ -209,7 +209,7 @@ suspend fun fetchEbicsC5x(
this.bankConnection = conn
this.code = "C53"
this.messageId = msgId
- this.message = SerialBlob(it.second.toByteArray(Charsets.UTF_8))
+ this.message = ExposedBlob(it.second.toByteArray(Charsets.UTF_8))
}
}
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt
@@ -60,6 +60,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.time.delay
import org.jetbrains.exposed.sql.and
+import org.jetbrains.exposed.sql.statements.api.ExposedBlob
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -73,7 +74,6 @@ import java.time.Duration
import java.util.*
import java.util.zip.InflaterInputStream
import javax.crypto.EncryptedPrivateKeyInfo
-import javax.sql.rowset.serial.SerialBlob
import java.time.LocalDateTime
data class NexusError(val statusCode: HttpStatusCode, val reason: String) :
@@ -176,9 +176,9 @@ fun createEbicsBankConnectionFromBackup(
hostID = ebicsBackup.hostID
partnerID = ebicsBackup.partnerID
userID = ebicsBackup.userID
- signaturePrivateKey = SerialBlob(sigKey.encoded)
- encryptionPrivateKey = SerialBlob(encKey.encoded)
- authenticationPrivateKey = SerialBlob(authKey.encoded)
+ signaturePrivateKey = ExposedBlob(sigKey.encoded)
+ encryptionPrivateKey = ExposedBlob((encKey.encoded))
+ authenticationPrivateKey = ExposedBlob((authKey.encoded))
nexusBankConnection = bankConn
ebicsIniState = EbicsInitState.UNKNOWN
ebicsHiaState = EbicsInitState.UNKNOWN
@@ -222,9 +222,9 @@ fun createEbicsBankConnection(bankConnectionName: String, user: NexusUserEntity,
partnerID = newTransportData.partnerID
userID = newTransportData.userID
systemID = newTransportData.systemID
- signaturePrivateKey = SerialBlob(pairA.private.encoded)
- encryptionPrivateKey = SerialBlob(pairB.private.encoded)
- authenticationPrivateKey = SerialBlob(pairC.private.encoded)
+ signaturePrivateKey = ExposedBlob((pairA.private.encoded))
+ encryptionPrivateKey = ExposedBlob((pairB.private.encoded))
+ authenticationPrivateKey = ExposedBlob((pairC.private.encoded))
nexusBankConnection = bankConn
ebicsIniState = EbicsInitState.NOT_SENT
ebicsHiaState = EbicsInitState.NOT_SENT
@@ -278,6 +278,18 @@ suspend fun downloadFacadesTransactions(coroutineScope: CoroutineScope) {
}
}
+fun <T>expectNonNull(param: T?): T {
+ return param ?: throw EbicsProtocolError(
+ HttpStatusCode.BadRequest,
+ "Non-null value expected."
+ )
+}
+
+fun ApplicationCall.expectUrlParameter(name: String): String {
+ return this.request.queryParameters[name]
+ ?: throw EbicsProtocolError(HttpStatusCode.BadRequest, "Parameter '$name' not provided in URI")
+}
+
suspend fun fetchTransactionsInternal(
client: HttpClient,
user: NexusUserEntity,
@@ -860,8 +872,8 @@ fun serverMain(dbName: String) {
subscriberEntity.ebicsHiaState = EbicsInitState.SENT
}
if (hpbData != null) {
- subscriberEntity.bankAuthenticationPublicKey = SerialBlob(hpbData.authenticationPubKey.encoded)
- subscriberEntity.bankEncryptionPublicKey = SerialBlob(hpbData.encryptionPubKey.encoded)
+ subscriberEntity.bankAuthenticationPublicKey = ExposedBlob((hpbData.authenticationPubKey.encoded))
+ subscriberEntity.bankEncryptionPublicKey = ExposedBlob((hpbData.encryptionPubKey.encoded))
}
}
call.respond(object {})
@@ -872,7 +884,7 @@ fun serverMain(dbName: String) {
val list = BankMessageList()
val conn = requireBankConnection(call, "connid")
NexusBankMessageEntity.find { NexusBankMessagesTable.bankConnection eq conn.id }.map {
- list.bankMessages.add(BankMessageInfo(it.messageId, it.code, it.message.length()))
+ list.bankMessages.add(BankMessageInfo(it.messageId, it.code, it.message.bytes.size.toLong()))
}
list
}
@@ -890,7 +902,7 @@ fun serverMain(dbName: String) {
throw NexusError(HttpStatusCode.NotFound, "bank message not found")
}
return@transaction object {
- val msgContent = msg.message.toByteArray()
+ val msgContent = msg.message.bytes
}
}
call.respondBytes(ret.msgContent, ContentType("application", "xml"))
@@ -974,8 +986,8 @@ fun serverMain(dbName: String) {
val conn = requireBankConnection(call, "connid")
val subscriber =
EbicsSubscriberEntity.find { EbicsSubscribersTable.nexusBankConnection eq conn.id }.first()
- subscriber.bankAuthenticationPublicKey = SerialBlob(hpbData.authenticationPubKey.encoded)
- subscriber.bankEncryptionPublicKey = SerialBlob(hpbData.encryptionPubKey.encoded)
+ subscriber.bankAuthenticationPublicKey = ExposedBlob((hpbData.authenticationPubKey.encoded))
+ subscriber.bankEncryptionPublicKey = ExposedBlob((hpbData.encryptionPubKey.encoded))
}
call.respond(object {})
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/taler.kt
@@ -12,16 +12,13 @@ import io.ktor.response.respondText
import io.ktor.routing.Route
import io.ktor.routing.get
import io.ktor.routing.post
-import org.apache.http.client.methods.RequestBuilder.post
import org.jetbrains.exposed.dao.Entity
-import org.jetbrains.exposed.dao.IdTable
+import org.jetbrains.exposed.dao.id.IdTable
import org.jetbrains.exposed.sql.*
-import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction
-import tech.libeufin.util.*
-import java.time.LocalDateTime
-import java.time.ZoneId
-import java.util.concurrent.atomic.LongAdder
+import tech.libeufin.util.CryptoUtil
+import tech.libeufin.util.EbicsProtocolError
+import tech.libeufin.util.parseAmount
import kotlin.math.abs
import kotlin.math.min
@@ -53,6 +50,7 @@ data class TalerIncomingBankTransaction(
data class TalerIncomingHistory(
var incoming_transactions: MutableList<TalerIncomingBankTransaction> = mutableListOf()
)
+
data class TalerOutgoingBankTransaction(
val row_id: Long,
val date: GnunetTimestamp, // timestamp
@@ -83,6 +81,7 @@ data class TalerAdminAddIncoming(
data class GnunetTimestamp(
val t_ms: Long
)
+
data class TalerAddIncomingResponse(
val timestamp: GnunetTimestamp,
val row_id: Long
@@ -146,6 +145,7 @@ fun <T : Entity<Long>> SizedIterable<T>.orderTaler(delta: Int): List<T> {
fun buildPaytoUri(name: String, iban: String, bic: String): String {
return "payto://iban/$bic/$iban?name=$name"
}
+
fun buildPaytoUri(iban: String, bic: String): String {
return "payto://iban/$bic/$iban"
}
@@ -162,23 +162,22 @@ fun getComparisonOperator(delta: Int, start: Long, table: IdTable<Long>): Op<Boo
}
}
}
+
+fun expectLong(param: String?): Long {
+ if (param == null) {
+ throw EbicsProtocolError(HttpStatusCode.BadRequest, "'$param' is not Long")
+ }
+ return try {
+ param.toLong()
+ } catch (e: Exception) {
+ throw EbicsProtocolError(HttpStatusCode.BadRequest, "'$param' is not Long")
+ }
+}
+
+
/** Helper handling 'start' being optional and its dependence on 'delta'. */
fun handleStartArgument(start: String?, delta: Int): Long {
- return expectLong(start) ?: if (delta >= 0) {
- /**
- * Using -1 as the smallest value, as some DBMS might use 0 and some
- * others might use 1 as the smallest row id.
- */
- -1
- } else {
- /**
- * NOTE: the database currently enforces there MAX_VALUE is always
- * strictly greater than any row's id in the database. In fact, the
- * database throws exception whenever a new row is going to occupy
- * the MAX_VALUE with its id.
- */
- Long.MAX_VALUE
- }
+ return expectLong(start)
}
/**
@@ -405,13 +404,19 @@ fun ingestTalerTransactions() {
}
suspend fun historyOutgoing(call: ApplicationCall): Unit {
- val delta: Int = expectInt(call.expectUrlParameter("delta"))
+ val param = call.expectUrlParameter("delta")
+ val delta: Int = try {
+ param.toInt()
+ } catch (e: Exception) {
+ throw EbicsProtocolError(HttpStatusCode.BadRequest, "'${param}' is not Int")
+ }
val start: Long = handleStartArgument(call.request.queryParameters["start"], delta)
val startCmpOp = getComparisonOperator(delta, start, TalerRequestedPayments)
/* retrieve database elements */
val history = TalerOutgoingHistory()
transaction {
val user = authenticateRequest(call.request)
+
/** Retrieve all the outgoing payments from the _clean Taler outgoing table_ */
val subscriberBankAccount = getFacadeBankAccount(user)
val reqPayments = TalerRequestedPaymentEntity.find {
@@ -424,8 +429,11 @@ suspend fun historyOutgoing(call: ApplicationCall): Unit {
row_id = it.id.value,
amount = it.amount,
wtid = it.wtid,
- date = GnunetTimestamp(it.rawConfirmed?.bookingDate?.div(1000) ?: throw NexusError(
- HttpStatusCode.InternalServerError, "Null value met after check, VERY strange.")),
+ date = GnunetTimestamp(
+ it.rawConfirmed?.bookingDate?.div(1000) ?: throw NexusError(
+ HttpStatusCode.InternalServerError, "Null value met after check, VERY strange."
+ )
+ ),
credit_account = it.creditAccount,
debit_account = buildPaytoUri(subscriberBankAccount.iban, subscriberBankAccount.bankCode),
exchange_base_url = "FIXME-to-request-along-subscriber-registration"
@@ -442,7 +450,12 @@ suspend fun historyOutgoing(call: ApplicationCall): Unit {
// /taler/history/incoming
suspend fun historyIncoming(call: ApplicationCall): Unit {
- val delta: Int = expectInt(call.expectUrlParameter("delta"))
+ val param = call.expectUrlParameter("delta")
+ val delta: Int = try {
+ param.toInt()
+ } catch (e: Exception) {
+ throw EbicsProtocolError(HttpStatusCode.BadRequest, "'${param}' is not Int")
+ }
val start: Long = handleStartArgument(call.request.queryParameters["start"], delta)
val history = TalerIncomingHistory()
val startCmpOp = getComparisonOperator(delta, start, TalerIncomingPayments)
diff --git a/sandbox/build.gradle b/sandbox/build.gradle
@@ -41,10 +41,10 @@ sourceSets {
}
def ktor_version = "1.3.2"
+def exposed_version = "0.24.1"
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
- implementation "org.jetbrains.exposed:exposed:0.17.6"
implementation "ch.qos.logback:logback-classic:1.2.3"
implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'
implementation "javax.xml.bind:jaxb-api:2.3.0"
@@ -56,6 +56,10 @@ dependencies {
implementation group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.20'
+ implementation "org.jetbrains.exposed:exposed-core:$exposed_version"
+ implementation "org.jetbrains.exposed:exposed-dao:$exposed_version"
+ implementation "org.jetbrains.exposed:exposed-jdbc:$exposed_version"
+
implementation "io.ktor:ktor-server-core:$ktor_version"
implementation "io.ktor:ktor-client-apache:$ktor_version"
implementation "io.ktor:ktor-server-netty:$ktor_version"
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/DB.kt
@@ -19,8 +19,17 @@
package tech.libeufin.sandbox
-import org.jetbrains.exposed.dao.*
-import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.dao.Entity
+import org.jetbrains.exposed.dao.EntityClass
+import org.jetbrains.exposed.dao.IntEntity
+import org.jetbrains.exposed.dao.IntEntityClass
+import org.jetbrains.exposed.dao.id.EntityID
+import org.jetbrains.exposed.dao.id.IdTable
+import org.jetbrains.exposed.dao.id.IntIdTable
+import org.jetbrains.exposed.sql.Database
+import org.jetbrains.exposed.sql.SchemaUtils
+import org.jetbrains.exposed.sql.StdOutSqlLogger
+import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.transactions.TransactionManager
import org.jetbrains.exposed.sql.transactions.transaction
import java.sql.Connection
@@ -85,6 +94,7 @@ object EbicsSubscriberPublicKeysTable : IntIdTable() {
val rsaPublicKey = blob("rsaPublicKey")
val state = enumeration("state", KeyState::class)
}
+
class EbicsSubscriberPublicKeyEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<EbicsSubscriberPublicKeyEntity>(EbicsSubscriberPublicKeysTable)
@@ -102,8 +112,10 @@ object EbicsHostsTable : IntIdTable() {
val encryptionPrivateKey = blob("encryptionPrivateKey")
val authenticationPrivateKey = blob("authenticationPrivateKey")
}
+
class EbicsHostEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<EbicsHostEntity>(EbicsHostsTable)
+
var hostId by EbicsHostsTable.hostID
var ebicsVersion by EbicsHostsTable.ebicsVersion
var signaturePrivateKey by EbicsHostsTable.signaturePrivateKey
@@ -125,8 +137,10 @@ object EbicsSubscribersTable : IntIdTable() {
val nextOrderID = integer("nextOrderID")
val state = enumeration("state", SubscriberState::class)
}
+
class EbicsSubscriberEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<EbicsSubscriberEntity>(EbicsSubscribersTable)
+
var userId by EbicsSubscribersTable.userId
var partnerId by EbicsSubscribersTable.partnerId
var systemId by EbicsSubscribersTable.systemId
@@ -152,8 +166,10 @@ object EbicsDownloadTransactionsTable : IdTable<String>() {
val segmentSize = integer("segmentSize")
val receiptReceived = bool("receiptReceived")
}
+
class EbicsDownloadTransactionEntity(id: EntityID<String>) : Entity<String>(id) {
companion object : EntityClass<String, EbicsDownloadTransactionEntity>(EbicsDownloadTransactionsTable)
+
var orderType by EbicsDownloadTransactionsTable.orderType
var host by EbicsHostEntity referencedOn EbicsDownloadTransactionsTable.host
var subscriber by EbicsSubscriberEntity referencedOn EbicsDownloadTransactionsTable.subscriber
@@ -177,8 +193,10 @@ object EbicsUploadTransactionsTable : IdTable<String>() {
val lastSeenSegment = integer("lastSeenSegment")
val transactionKeyEnc = blob("transactionKeyEnc")
}
+
class EbicsUploadTransactionEntity(id: EntityID<String>) : Entity<String>(id) {
companion object : EntityClass<String, EbicsUploadTransactionEntity>(EbicsUploadTransactionsTable)
+
var orderType by EbicsUploadTransactionsTable.orderType
var orderID by EbicsUploadTransactionsTable.orderID
var host by EbicsHostEntity referencedOn EbicsUploadTransactionsTable.host
@@ -199,8 +217,10 @@ object EbicsOrderSignaturesTable : IntIdTable() {
val signatureAlgorithm = text("signatureAlgorithm")
val signatureValue = blob("signatureValue")
}
+
class EbicsOrderSignatureEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<EbicsOrderSignatureEntity>(EbicsOrderSignaturesTable)
+
var orderID by EbicsOrderSignaturesTable.orderID
var orderType by EbicsOrderSignaturesTable.orderType
var partnerID by EbicsOrderSignaturesTable.partnerID
@@ -218,8 +238,10 @@ object EbicsUploadTransactionChunksTable : IdTable<String>() {
val chunkIndex = integer("chunkIndex")
val chunkContent = blob("chunkContent")
}
+
class EbicsUploadTransactionChunkEntity(id: EntityID<String>) : Entity<String>(id) {
companion object : EntityClass<String, EbicsUploadTransactionChunkEntity>(EbicsUploadTransactionChunksTable)
+
var chunkIndex by EbicsUploadTransactionChunksTable.chunkIndex
var chunkContent by EbicsUploadTransactionChunksTable.chunkContent
}
@@ -234,13 +256,18 @@ object PaymentsTable : IntIdTable() {
val amount = text("amount")
val date = long("date")
}
+
class PaymentEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<PaymentEntity>(PaymentsTable)
+
var creditorIban by PaymentsTable.creditorIban
var debitorIban by PaymentsTable.debitorIban
var subject by PaymentsTable.subject
- var amount by PaymentsTable.amount /** in the CURRENCY:X.Y format */
- var date by PaymentsTable.date /** Date when the payment was persisted in this system. */
+ var amount by PaymentsTable.amount
+
+ /** in the CURRENCY:X.Y format */
+ var date by PaymentsTable.date
+ /** Date when the payment was persisted in this system. */
}
/**
@@ -254,8 +281,10 @@ object BankAccountsTable : IntIdTable() {
val label = text("label")
val subscriber = reference("subscriber", EbicsSubscribersTable)
}
+
class BankAccountEntity(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<BankAccountEntity>(BankAccountsTable)
+
var iban by BankAccountsTable.iban
var bic by BankAccountsTable.bic
var name by BankAccountsTable.name
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/EbicsProtocolBackend.kt
@@ -28,38 +28,23 @@ import io.ktor.response.respond
import io.ktor.response.respondText
import org.apache.xml.security.binding.xmldsig.RSAKeyValueType
import org.jetbrains.exposed.sql.*
+import org.jetbrains.exposed.sql.statements.api.ExposedBlob
import org.jetbrains.exposed.sql.transactions.transaction
import org.w3c.dom.Document
+import tech.libeufin.util.*
+import tech.libeufin.util.XMLUtil.Companion.signEbicsResponse
import tech.libeufin.util.ebics_h004.*
import tech.libeufin.util.ebics_hev.HEVResponse
import tech.libeufin.util.ebics_hev.SystemReturnCodeType
import tech.libeufin.util.ebics_s001.SignatureTypes
import tech.libeufin.util.ebics_s001.UserSignatureData
-import tech.libeufin.util.CryptoUtil
-import tech.libeufin.util.EbicsOrderUtil
-import tech.libeufin.util.XMLUtil
-import tech.libeufin.util.*
-import tech.libeufin.util.XMLUtil.Companion.signEbicsResponse
-import java.io.ByteArrayOutputStream
-import java.math.BigDecimal
import java.security.interfaces.RSAPrivateCrtKey
import java.security.interfaces.RSAPublicKey
+import java.time.Instant
+import java.time.LocalDateTime
import java.util.*
import java.util.zip.DeflaterInputStream
import java.util.zip.InflaterInputStream
-import javax.sql.rowset.serial.SerialBlob
-import javax.xml.datatype.DatatypeFactory
-import org.apache.commons.compress.utils.IOUtils
-import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
-import java.io.BufferedInputStream
-import java.io.ByteArrayInputStream
-import java.nio.charset.Charset
-import java.time.Instant
-import java.time.LocalDateTime
-import java.time.ZoneId
-import java.time.ZonedDateTime
-import java.time.format.DateTimeFormatter
-import javax.xml.datatype.XMLGregorianCalendar
open class EbicsRequestError(errorText: String, errorCode: String) :
@@ -142,6 +127,13 @@ private suspend fun ApplicationCall.respondEbicsKeyManagement(
respondText(text, ContentType.Application.Xml, HttpStatusCode.OK)
}
+fun <T>expectNonNull(x: T?): T {
+ if (x == null) {
+ throw EbicsProtocolError(HttpStatusCode.BadRequest, "expected non-null value")
+ }
+ return x;
+}
+
/**
* Returns a list of camt strings. Note: each element in the
* list accounts for only one payment in the history. In other
@@ -172,7 +164,10 @@ fun buildCamtString(type: Int, history: MutableList<RawPayment>): MutableList<St
root("Document") {
attribute("xmlns", "urn:iso:std:iso:20022:tech:xsd:camt.053.001.02")
attribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
- attribute("xsi:schemaLocation", "urn:iso:std:iso:20022:tech:xsd:camt.053.001.02 camt.053.001.02.xsd")
+ attribute(
+ "xsi:schemaLocation",
+ "urn:iso:std:iso:20022:tech:xsd:camt.053.001.02 camt.053.001.02.xsd"
+ )
element("BkToCstmrStmt") {
element("GrpHdr") {
element("MsgId") {
@@ -418,10 +413,11 @@ private fun constructCamtResponse(
transaction {
PaymentEntity.find {
PaymentsTable.creditorIban eq bankAccount.iban or
- (PaymentsTable.debitorIban eq bankAccount.iban) /**
- FIXME!
- and (PaymentsTable.date.between(start.millis, end.millis))
- */
+ (PaymentsTable.debitorIban eq bankAccount.iban)
+ /**
+ FIXME!
+ and (PaymentsTable.date.between(start.millis, end.millis))
+ */
}.forEach {
history.add(
RawPayment(
@@ -504,11 +500,11 @@ private suspend fun ApplicationCall.handleEbicsHia(header: EbicsUnsecuredRequest
throw EbicsInvalidRequestError()
}
ebicsSubscriber.authenticationKey = EbicsSubscriberPublicKeyEntity.new {
- this.rsaPublicKey = SerialBlob(authPub.encoded)
+ this.rsaPublicKey = ExposedBlob(authPub.encoded)
state = KeyState.NEW
}
ebicsSubscriber.encryptionKey = EbicsSubscriberPublicKeyEntity.new {
- this.rsaPublicKey = SerialBlob(encPub.encoded)
+ this.rsaPublicKey = ExposedBlob(encPub.encoded)
state = KeyState.NEW
}
ebicsSubscriber.state = when (ebicsSubscriber.state) {
@@ -539,7 +535,7 @@ private suspend fun ApplicationCall.handleEbicsIni(header: EbicsUnsecuredRequest
throw EbicsInvalidRequestError()
}
ebicsSubscriber.signatureKey = EbicsSubscriberPublicKeyEntity.new {
- this.rsaPublicKey = SerialBlob(sigPub.encoded)
+ this.rsaPublicKey = ExposedBlob(sigPub.encoded)
state = KeyState.NEW
}
ebicsSubscriber.state = when (ebicsSubscriber.state) {
@@ -570,16 +566,16 @@ private suspend fun ApplicationCall.handleEbicsHpb(
val encPubBlob = ebicsSubscriber.encryptionKey!!.rsaPublicKey
val sigPubBlob = ebicsSubscriber.signatureKey!!.rsaPublicKey
SubscriberKeys(
- CryptoUtil.loadRsaPublicKey(authPubBlob.toByteArray()),
- CryptoUtil.loadRsaPublicKey(encPubBlob.toByteArray()),
- CryptoUtil.loadRsaPublicKey(sigPubBlob.toByteArray())
+ CryptoUtil.loadRsaPublicKey(authPubBlob.bytes),
+ CryptoUtil.loadRsaPublicKey(encPubBlob.bytes),
+ CryptoUtil.loadRsaPublicKey(sigPubBlob.bytes)
)
}
val validationResult =
XMLUtil.verifyEbicsDocument(requestDocument, subscriberKeys.authenticationPublicKey)
LOGGER.info("validationResult: $validationResult")
if (!validationResult) {
- throw EbicsKeyManagementError("invalid signature", "90000");
+ throw EbicsKeyManagementError("invalid signature", "90000")
}
val hpbRespondeData = HPBResponseOrderData().apply {
this.authenticationPubKeyInfo = EbicsTypes.AuthenticationPubKeyInfoType().apply {
@@ -622,8 +618,8 @@ private fun ApplicationCall.ensureEbicsHost(requestHostID: String): EbicsHostPub
LOGGER.warn("client requested unknown HostID ${requestHostID}")
throw EbicsKeyManagementError("[EBICS_INVALID_HOST_ID]", "091011")
}
- val encryptionPrivateKey = CryptoUtil.loadRsaPrivateKey(ebicsHost.encryptionPrivateKey.toByteArray())
- val authenticationPrivateKey = CryptoUtil.loadRsaPrivateKey(ebicsHost.authenticationPrivateKey.toByteArray())
+ val encryptionPrivateKey = CryptoUtil.loadRsaPrivateKey(ebicsHost.encryptionPrivateKey.bytes)
+ val authenticationPrivateKey = CryptoUtil.loadRsaPrivateKey(ebicsHost.authenticationPrivateKey.bytes)
EbicsHostPublicInfo(
requestHostID,
CryptoUtil.getRsaPublicFromPrivate(encryptionPrivateKey),
@@ -797,7 +793,7 @@ private fun handleEbicsDownloadTransactionInitialization(requestContext: Request
this.host = requestContext.ebicsHost
this.orderType = orderType
this.segmentSize = segmentSize
- this.transactionKeyEnc = SerialBlob(enc.encryptedTransactionKey)
+ this.transactionKeyEnc = ExposedBlob(enc.encryptedTransactionKey)
this.encodedResponse = encodedResponse
this.numSegments = numSegments
this.receiptReceived = false
@@ -847,7 +843,7 @@ private fun handleEbicsUploadTransactionInitialization(requestContext: RequestCo
this.orderType = orderType
this.orderID = orderID
this.numSegments = numSegments.toInt()
- this.transactionKeyEnc = SerialBlob(transactionKeyEnc)
+ this.transactionKeyEnc = ExposedBlob(transactionKeyEnc)
}
val sigObj = XMLUtil.convertStringToJaxb<UserSignatureData>(plainSigData.toString(Charsets.UTF_8))
println("got UserSignatureData: ${plainSigData.toString(Charsets.UTF_8)}")
@@ -859,7 +855,7 @@ private fun handleEbicsUploadTransactionInitialization(requestContext: RequestCo
this.partnerID = sig.partnerID
this.userID = sig.userID
this.signatureAlgorithm = sig.signatureVersion
- this.signatureValue = SerialBlob(sig.signatureValue)
+ this.signatureValue = ExposedBlob(sig.signatureValue)
}
}
return EbicsResponse.createForUploadInitializationPhase(transactionID, orderID)
@@ -875,7 +871,7 @@ private fun handleEbicsUploadTransactionTransmission(requestContext: RequestCont
val encOrderData =
requestObject.body.dataTransfer?.orderData ?: throw EbicsInvalidRequestError()
val zippedData = CryptoUtil.decryptEbicsE002(
- uploadTransaction.transactionKeyEnc.toByteArray(),
+ uploadTransaction.transactionKeyEnc.bytes,
Base64.getDecoder().decode(encOrderData),
requestContext.hostEncPriv
)
@@ -883,18 +879,22 @@ private fun handleEbicsUploadTransactionTransmission(requestContext: RequestCont
InflaterInputStream(zippedData.inputStream()).use { it.readAllBytes() }
logger.debug("got upload data: ${unzippedData.toString(Charsets.UTF_8)}")
- val sigs = EbicsOrderSignatureEntity.find {
+ val sigs = EbicsOrderSignatureEntity.find {
(EbicsOrderSignaturesTable.orderID eq uploadTransaction.orderID) and
(EbicsOrderSignaturesTable.orderType eq uploadTransaction.orderType)
}
- if (sigs.count() == 0) {
+ if (sigs.count() == 0L) {
throw EbicsInvalidRequestError()
}
for (sig in sigs) {
if (sig.signatureAlgorithm == "A006") {
val signedData = CryptoUtil.digestEbicsOrderA006(unzippedData)
- val res1 = CryptoUtil.verifyEbicsA006(sig.signatureValue.toByteArray(), signedData, requestContext.clientSigPub)
+ val res1 = CryptoUtil.verifyEbicsA006(
+ sig.signatureValue.bytes,
+ signedData,
+ requestContext.clientSigPub
+ )
if (!res1) {
throw EbicsInvalidRequestError()
@@ -954,19 +954,17 @@ private fun makeReqestContext(requestObject: EbicsRequest): RequestContext {
throw EbicsSubscriberStateError()
val hostAuthPriv = CryptoUtil.loadRsaPrivateKey(
- ebicsHost.authenticationPrivateKey
- .toByteArray()
+ ebicsHost.authenticationPrivateKey.bytes
)
val hostEncPriv = CryptoUtil.loadRsaPrivateKey(
- ebicsHost.encryptionPrivateKey
- .toByteArray()
+ ebicsHost.encryptionPrivateKey.bytes
)
val clientAuthPub =
- CryptoUtil.loadRsaPublicKey(subscriber.authenticationKey!!.rsaPublicKey.toByteArray())
+ CryptoUtil.loadRsaPublicKey(subscriber.authenticationKey!!.rsaPublicKey.bytes)
val clientEncPub =
- CryptoUtil.loadRsaPublicKey(subscriber.encryptionKey!!.rsaPublicKey.toByteArray())
+ CryptoUtil.loadRsaPublicKey(subscriber.encryptionKey!!.rsaPublicKey.bytes)
val clientSigPub =
- CryptoUtil.loadRsaPublicKey(subscriber.signatureKey!!.rsaPublicKey.toByteArray())
+ CryptoUtil.loadRsaPublicKey(subscriber.signatureKey!!.rsaPublicKey.bytes)
return RequestContext(
hostAuthPriv = hostAuthPriv,
@@ -1056,7 +1054,8 @@ suspend fun ApplicationCall.ebicsweb() {
}
}
EbicsTypes.TransactionPhaseType.RECEIPT -> {
- val requestTransactionID = requestObject.header.static.transactionID ?: throw EbicsInvalidRequestError()
+ val requestTransactionID =
+ requestObject.header.static.transactionID ?: throw EbicsInvalidRequestError()
if (requestContext.downloadTransaction == null)
throw EbicsInvalidRequestError()
val receiptCode =
diff --git a/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt b/sandbox/src/main/kotlin/tech/libeufin/sandbox/Main.kt
@@ -50,7 +50,6 @@ import java.lang.ArithmeticException
import java.math.BigDecimal
import java.security.interfaces.RSAPublicKey
import java.text.DateFormat
-import javax.sql.rowset.serial.SerialBlob
import javax.xml.bind.JAXBContext
import com.fasterxml.jackson.core.util.DefaultIndenter
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter
@@ -61,6 +60,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.ktor.http.toHttpDateString
+import org.jetbrains.exposed.sql.statements.api.ExposedBlob
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
@@ -272,9 +272,9 @@ fun main() {
EbicsHostEntity.new {
this.ebicsVersion = req.ebicsVersion
this.hostId = req.hostID
- this.authenticationPrivateKey = SerialBlob(pairA.private.encoded)
- this.encryptionPrivateKey = SerialBlob(pairB.private.encoded)
- this.signaturePrivateKey = SerialBlob(pairC.private.encoded)
+ this.authenticationPrivateKey = ExposedBlob(pairA.private.encoded)
+ this.encryptionPrivateKey = ExposedBlob(pairB.private.encoded)
+ this.signaturePrivateKey = ExposedBlob(pairC.private.encoded)
}
}
diff --git a/util/build.gradle b/util/build.gradle
@@ -26,9 +26,10 @@ sourceSets {
main.java.srcDirs = ['src/main/java', 'src/main/kotlin']
}
+def exposed_version = "0.24.1"
+
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
- implementation "org.jetbrains.exposed:exposed:0.17.6"
implementation "io.ktor:ktor-server-netty:1.2.4"
implementation "ch.qos.logback:logback-classic:1.2.3"
implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'
@@ -38,9 +39,11 @@ dependencies {
implementation "org.glassfish.jaxb:jaxb-runtime:2.3.1"
implementation 'org.apache.santuario:xmlsec:2.1.4'
implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.64'
- implementation group: 'org.xerial', name: 'sqlite-jdbc', version: '3.28.0'
implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.20'
+ implementation "org.jetbrains.exposed:exposed-core:$exposed_version"
+ implementation "org.jetbrains.exposed:exposed-dao:$exposed_version"
+
testImplementation group: 'junit', name: 'junit', version: '4.12'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit:1.3.50'
testImplementation 'org.jetbrains.kotlin:kotlin-test:1.3.50'
diff --git a/util/src/main/kotlin/DBTypes.kt b/util/src/main/kotlin/DBTypes.kt
@@ -1,7 +1,5 @@
package tech.libeufin.util
-import org.jetbrains.exposed.dao.IdTable
-import org.jetbrains.exposed.dao.IntIdTable
import org.jetbrains.exposed.sql.Column
import org.jetbrains.exposed.sql.ColumnType
import org.jetbrains.exposed.sql.Table
@@ -58,6 +56,6 @@ class AmountColumnType : ColumnType() {
* Make sure the number entered by upper layers does not need any rounding
* to conform to scale == 2
*/
-fun IdTable<*>.amount(name: String): Column<Amount> {
+fun Table.amount(name: String): Column<Amount> {
return registerColumn(name, AmountColumnType())
}
\ No newline at end of file
diff --git a/util/src/main/kotlin/ParametersChecks.kt b/util/src/main/kotlin/ParametersChecks.kt
@@ -1,40 +0,0 @@
-package tech.libeufin.util
-
-import io.ktor.application.ApplicationCall
-import io.ktor.http.HttpStatusCode
-
-fun expectInt(param: String): Int {
- return try {
- param.toInt()
- } catch (e: Exception) {
- throw EbicsProtocolError(HttpStatusCode.BadRequest,"'$param' is not Int")
- }
-}
-
-fun <T>expectNonNull(param: T?): T {
- return param ?: throw EbicsProtocolError(
- HttpStatusCode.BadRequest,
- "Non-null value expected."
- )
-}
-
-fun expectLong(param: String): Long {
- return try {
- param.toLong()
- } catch (e: Exception) {
- throw EbicsProtocolError(HttpStatusCode.BadRequest,"'$param' is not Long")
- }
-}
-
-fun expectLong(param: String?): Long? {
- if (param != null) {
- return expectLong(param)
- }
- return null
-}
-
-
-fun ApplicationCall.expectUrlParameter(name: String): String {
- return this.request.queryParameters[name]
- ?: throw EbicsProtocolError(HttpStatusCode.BadRequest, "Parameter '$name' not provided in URI")
-}
-\ No newline at end of file
diff --git a/util/src/main/kotlin/blob.kt b/util/src/main/kotlin/blob.kt
@@ -1,7 +0,0 @@
-package tech.libeufin.util
-
-import java.sql.Blob
-
-fun Blob.toByteArray(): ByteArray {
- return this.binaryStream.readAllBytes()
-}
-\ No newline at end of file