libeufin

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

commit febabaeb08dcb1a51ea0ecb73d8404027081568c
parent 7033a39afafcfa430e5a9bea57ac022f13905e97
Author: Marcello Stanisci <ms@taler.net>
Date:   Mon, 11 May 2020 01:56:36 +0200

POST ../bank-transports (incomplete).

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/DB.kt | 13++++++-------
Mnexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt | 17+----------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt | 46++++++++++++++++++++++++++++++----------------
Mnexus/src/main/kotlin/tech/libeufin/nexus/Main.kt | 85++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 121 insertions(+), 40 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/DB.kt @@ -177,7 +177,8 @@ class BankAccountEntity(id: EntityID<String>) : Entity<String>(id) { var bankCode by BankAccountsTable.bankCode } -object EbicsSubscribersTable : IntIdTable() { +object EbicsSubscribersTable : IdTable<String>() { + override val id = varchar("id", ID_MAX_LENGTH).entityId().primaryKey() val ebicsURL = text("ebicsURL") val hostID = text("hostID") val partnerID = text("partnerID") @@ -188,10 +189,11 @@ object EbicsSubscribersTable : IntIdTable() { val authenticationPrivateKey = blob("authenticationPrivateKey") val bankEncryptionPublicKey = blob("bankEncryptionPublicKey").nullable() val bankAuthenticationPublicKey = blob("bankAuthenticationPublicKey").nullable() + var nexusUser = reference("nexusUser", NexusUsersTable) } -class EbicsSubscriberEntity(id: EntityID<Int>) : Entity<Int>(id) { - companion object : EntityClass<Int, EbicsSubscriberEntity>(EbicsSubscribersTable) +class EbicsSubscriberEntity(id: EntityID<String>) : Entity<String>(id) { + companion object : EntityClass<String, EbicsSubscriberEntity>(EbicsSubscribersTable) var ebicsURL by EbicsSubscribersTable.ebicsURL var hostID by EbicsSubscribersTable.hostID var partnerID by EbicsSubscribersTable.partnerID @@ -202,19 +204,16 @@ class EbicsSubscriberEntity(id: EntityID<Int>) : Entity<Int>(id) { var authenticationPrivateKey by EbicsSubscribersTable.authenticationPrivateKey var bankEncryptionPublicKey by EbicsSubscribersTable.bankEncryptionPublicKey var bankAuthenticationPublicKey by EbicsSubscribersTable.bankAuthenticationPublicKey + var nexusUser by NexusUserEntity referencedOn EbicsSubscribersTable.nexusUser } object NexusUsersTable : IdTable<String>() { override val id = varchar("id", ID_MAX_LENGTH).entityId() - val ebicsSubscriber = reference("ebicsSubscriber", EbicsSubscribersTable).nullable() - val testSubscriber = reference("testSubscriber", EbicsSubscribersTable).nullable() val password = blob("password").nullable() } class NexusUserEntity(id: EntityID<String>) : Entity<String>(id) { companion object : EntityClass<String, NexusUserEntity>(NexusUsersTable) - var ebicsSubscriber by EbicsSubscriberEntity optionalReferencedOn NexusUsersTable.ebicsSubscriber - var testSubscriber by EbicsSubscriberEntity optionalReferencedOn NexusUsersTable.testSubscriber var password by NexusUsersTable.password } diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Helpers.kt @@ -127,23 +127,8 @@ fun getSubscriberDetailsInternal(subscriber: EbicsSubscriberEntity): EbicsClient ) } -/** Return non null Ebics subscriber, or throw error otherwise. */ -fun getEbicsSubscriberFromUser(nexusUser: NexusUserEntity): EbicsSubscriberEntity { - return nexusUser.ebicsSubscriber ?: throw NexusError( - HttpStatusCode.NotFound, - "Ebics subscriber was never activated" - ) -} +fun getTransport() -fun getSubscriberDetailsFromNexusUserId(id: String): EbicsClientSubscriberDetails { - return transaction { - val nexusUser = extractNexusUser(id) - getSubscriberDetailsInternal(nexusUser.ebicsSubscriber ?: throw NexusError( - HttpStatusCode.NotFound, - "Cannot get details for non-activated subscriber!" - )) - } -} /** * Create a PAIN.001 XML document according to the input data. diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/JSON.kt @@ -32,22 +32,6 @@ data class EbicsDateRangeJson( val end: String? ) -/** - * This object is used twice: as a response to the backup request, - * and as a request to the backup restore. Note: in the second case - * the client must provide the passphrase. - */ -data class EbicsKeysBackupJson( - val userID: String, - val partnerID: String, - val hostID: String, - val ebicsURL: String, - val authBlob: String, - val encBlob: String, - val sigBlob: String, - val passphrase: String? = null -) - data class EbicsPubKeyInfo( val authPub: String, val encPub: String, @@ -94,6 +78,36 @@ data class RawPayments( /************************************************* * API types (used as requests/responses types) * *************************************************/ +data class BankTransport( + val name: String, + val backup: Any, // only EbicsKeysBackupJson exists now. + val new: Any, + val type: String +) + +/** + * This object is used twice: as a response to the backup request, + * and as a request to the backup restore. Note: in the second case + * the client must provide the passphrase. + */ +data class EbicsKeysBackupJson( + val userID: String, + val partnerID: String, + val hostID: String, + val ebicsURL: String, + val authBlob: String, + val encBlob: String, + val sigBlob: String, + val passphrase: String +) + +data class EbicsNewTransport( + val userID: String, + val partnerID: String, + val hostID: String, + val ebicsURL: String, + val systemID: String +) /** Response type of "GET /prepared-payments/{uuid}" */ data class PaymentStatus( diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Main.kt @@ -52,6 +52,7 @@ import org.slf4j.event.Level import tech.libeufin.util.* import java.text.DateFormat import java.util.zip.InflaterInputStream +import javax.crypto.EncryptedPrivateKeyInfo import javax.sql.rowset.serial.SerialBlob data class NexusError(val statusCode: HttpStatusCode, val reason: String) : Exception() @@ -197,7 +198,7 @@ fun main() { val pain001document = createPain001document(preparedPayment) when (body.transport) { "ebics" -> { - val subscriberDetails = getSubscriberDetailsFromNexusUserId(userId) + val subscriberDetails = getTransport(body.transport) logger.debug("Uploading PAIN.001: ${pain001document}") doEbicsUploadTransaction( client, @@ -377,6 +378,7 @@ fun main() { amount = "${it.currency}:${it.amount}" ) ) + } } return@get @@ -385,6 +387,87 @@ fun main() { * Adds a new bank transport. */ post("/bank-transports") { + val userId = authenticateRequest(call.request.headers["Authorization"]) + // user exists and is authenticated. + val body = call.receive<BankTransport>() + when (body.backup) { + is EbicsKeysBackupJson -> { + val (authKey, encKey, sigKey) = try { + Triple( + CryptoUtil.decryptKey( + EncryptedPrivateKeyInfo(base64ToBytes(body.backup.authBlob)), + body.backup.passphrase + ), + CryptoUtil.decryptKey( + EncryptedPrivateKeyInfo(base64ToBytes(body.backup.encBlob)), + body.backup.passphrase + ), + CryptoUtil.decryptKey( + EncryptedPrivateKeyInfo(base64ToBytes(body.backup.sigBlob)), + body.backup.passphrase + ) + ) + } catch (e: Exception) { + e.printStackTrace() + logger.info("Restoring keys failed, probably due to wrong passphrase") + throw NexusError( + HttpStatusCode.BadRequest, + "Bad backup given" + ) + } + logger.info("Restoring keys, creating new user: $userId") + try { + transaction { + EbicsSubscriberEntity.new(userId) { + this.nexusUser = extractNexusUser(userId) + ebicsURL = body.backup.ebicsURL + hostID = body.backup.hostID + partnerID = body.backup.partnerID + userID = body.backup.userID + signaturePrivateKey = SerialBlob(sigKey.encoded) + encryptionPrivateKey = SerialBlob(encKey.encoded) + authenticationPrivateKey = SerialBlob(authKey.encoded) + } + } + } catch (e: Exception) { + print(e) + call.respond( + NexusErrorJson("Could not store the new account into database") + ) + return@post + } + } + } + when (body.new) { + is EbicsNewTransport -> { + val pairA = CryptoUtil.generateRsaKeyPair(2048) + val pairB = CryptoUtil.generateRsaKeyPair(2048) + val pairC = CryptoUtil.generateRsaKeyPair(2048) + transaction { + EbicsSubscriberEntity.new { + nexusUser = extractNexusUser(userId) + ebicsURL = body.new.ebicsURL + hostID = body.new.hostID + partnerID = body.new.partnerID + userID = body.new.userID + systemID = body.new.systemID + signaturePrivateKey = SerialBlob(pairA.private.encoded) + encryptionPrivateKey = SerialBlob(pairB.private.encoded) + authenticationPrivateKey = SerialBlob(pairC.private.encoded) + } + } + call.respondText( + "EBICS user successfully created", + ContentType.Text.Plain, + HttpStatusCode.OK + ) + return@post + } + } + call.respond( + HttpStatusCode.BadRequest, + "Neither backup nor new transport given in request" + ) return@post } /**