libeufin

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

commit 551831843d5f8e6651020cdc7bada26c4d63d4cf
parent 58d6b0aa414ca6a8a137ff6c4b301f502e8eccab
Author: Antoine A <>
Date:   Wed,  1 Nov 2023 14:14:50 +0000

Fix add_incoming auth and clean tests

Diffstat:
Mbank/src/main/kotlin/tech/libeufin/bank/Database.kt | 53+++++++++++++++++------------------------------------
Mbank/src/main/kotlin/tech/libeufin/bank/WireGatewayApi.kt | 2+-
Mbank/src/test/kotlin/StatsTest.kt | 7++-----
Mbank/src/test/kotlin/WireGatewayApiTest.kt | 63++++++++++++++++++++++++++++-----------------------------------
Mbank/src/test/kotlin/helpers.kt | 9+++++++++
5 files changed, 57 insertions(+), 77 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt @@ -398,7 +398,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f amount = TalerAmount( it.getLong("balance_val"), it.getInt("balance_frac"), - getCurrency() + bankCurrency ), credit_debit_indicator = if (it.getBoolean("has_debt")) { @@ -410,7 +410,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f debit_threshold = TalerAmount( value = it.getLong("max_debt_val"), frac = it.getInt("max_debt_frac"), - getCurrency() + bankCurrency ) ) } @@ -543,7 +543,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f amount = TalerAmount( value = it.getLong("balance_val"), frac = it.getInt("balance_frac"), - currency = getCurrency() + currency = bankCurrency ), credit_debit_indicator = if (it.getBoolean("balance_has_debt")) { CorebankCreditDebitInfo.debit @@ -554,7 +554,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f debit_threshold = TalerAmount( value = it.getLong("max_debt_val"), frac = it.getInt("max_debt_frac"), - currency = getCurrency() + currency = bankCurrency ) ) } @@ -562,25 +562,6 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f // BANK ACCOUNTS - suspend fun bankAccountSetMaxDebt( - owningCustomerId: Long, - maxDebt: TalerAmount - ): Boolean = conn { conn -> - val stmt = conn.prepareStatement(""" - UPDATE bank_accounts - SET max_debt=(?,?)::taler_amount - WHERE owning_customer_id=? - """) - stmt.setLong(1, maxDebt.value) - stmt.setInt(2, maxDebt.frac) - stmt.setLong(3, owningCustomerId) - stmt.executeUpdateViolation() - } - - private fun getCurrency(): String { - return bankCurrency - } - suspend fun bankAccountGetFromOwnerId(ownerId: Long): BankAccount? = conn { conn -> val stmt = conn.prepareStatement(""" SELECT @@ -605,7 +586,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f balance = TalerAmount( it.getLong("balance_val"), it.getInt("balance_frac"), - getCurrency() + bankCurrency ), owningCustomerId = it.getLong("owning_customer_id"), hasDebt = it.getBoolean("has_debt"), @@ -614,7 +595,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f maxDebt = TalerAmount( value = it.getLong("max_debt_val"), frac = it.getInt("max_debt_frac"), - getCurrency() + bankCurrency ), bankAccountId = it.getLong("bank_account_id") ) @@ -647,7 +628,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f balance = TalerAmount( it.getLong("balance_val"), it.getInt("balance_frac"), - getCurrency() + bankCurrency ), owningCustomerId = it.getLong("owning_customer_id"), hasDebt = it.getBoolean("has_debt"), @@ -655,7 +636,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f maxDebt = TalerAmount( value = it.getLong("max_debt_val"), frac = it.getInt("max_debt_frac"), - getCurrency() + bankCurrency ), isPublic = it.getBoolean("is_public"), bankAccountId = it.getLong("bank_account_id") @@ -782,7 +763,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f amount = TalerAmount( it.getLong("amount_val"), it.getInt("amount_frac"), - getCurrency() + bankCurrency ), accountServicerReference = it.getString("account_servicer_reference"), endToEndId = it.getString("end_to_end_id"), @@ -881,7 +862,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f amount = TalerAmount( it.getLong("amount_val"), it.getInt("amount_frac"), - getCurrency() + bankCurrency ), subject = it.getString("subject"), direction = TransactionDirection.valueOf(it.getString("direction")) @@ -913,7 +894,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f amount = TalerAmount( it.getLong("amount_val"), it.getInt("amount_frac"), - getCurrency() + bankCurrency ), debit_account = it.getString("debtor_payto_uri"), reserve_pub = EddsaPublicKey(it.getBytes("reserve_pub")), @@ -946,7 +927,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f amount = TalerAmount( it.getLong("amount_val"), it.getInt("amount_frac"), - getCurrency() + bankCurrency ), credit_account = IbanPayTo(it.getString("creditor_payto_uri")), wtid = ShortHashCode(it.getBytes("wtid")), @@ -1005,7 +986,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f amount = TalerAmount( it.getLong("amount_val"), it.getInt("amount_frac"), - getCurrency() + bankCurrency ), selectionDone = it.getBoolean("selection_done"), selectedExchangePayto = it.getString("selected_exchange_payto")?.run(::IbanPayTo), @@ -1284,19 +1265,19 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f amountDebit = TalerAmount( value = it.getLong("amount_debit_val"), frac = it.getInt("amount_debit_frac"), - getCurrency() + bankCurrency ), amountCredit = TalerAmount( value = it.getLong("amount_credit_val"), frac = it.getInt("amount_credit_frac"), - getCurrency() + bankCurrency ), bankAccount = it.getLong("bank_account"), buyAtRatio = it.getInt("buy_at_ratio"), buyInFee = TalerAmount( value = it.getLong("buy_in_fee_val"), frac = it.getInt("buy_in_fee_frac"), - getCurrency() + bankCurrency ), credit_payto_uri = it.getString("credit_payto_uri"), cashoutCurrency = it.getString("cashout_currency"), @@ -1306,7 +1287,7 @@ class Database(dbConfig: String, private val bankCurrency: String, private val f sellOutFee = TalerAmount( value = it.getLong("sell_out_fee_val"), frac = it.getInt("sell_out_fee_frac"), - getCurrency() + bankCurrency ), subject = it.getString("subject"), tanChannel = TanChannel.valueOf(it.getString("tan_channel")), diff --git a/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/WireGatewayApi.kt @@ -114,7 +114,7 @@ fun Routing.wireGatewayApi(db: Database, ctx: BankConfig) { historyEndpoint(::OutgoingHistory, Database::exchangeOutgoingPoolHistory) } } - auth(db, TokenScope.readwrite) { + authAdmin(db, TokenScope.readwrite) { post("/accounts/{USERNAME}/taler-wire-gateway/admin/add-incoming") { val req = call.receive<AddIncomingRequest>() ctx.checkInternalCurrency(req.amount) diff --git a/bank/src/test/kotlin/StatsTest.kt b/bank/src/test/kotlin/StatsTest.kt @@ -32,11 +32,8 @@ import tech.libeufin.util.* class StatsTest { @Test - fun transfer() = bankSetup { db -> - assert(db.bankAccountSetMaxDebt( - 2L, - TalerAmount(1000, 0, "KUDOS") - )) + fun transfer() = bankSetup { _ -> + setMaxDebt("exchange", TalerAmount("KUDOS:1000")) suspend fun transfer(amount: TalerAmount) { client.post("/accounts/exchange/taler-wire-gateway/transfer") { diff --git a/bank/src/test/kotlin/WireGatewayApiTest.kt b/bank/src/test/kotlin/WireGatewayApiTest.kt @@ -60,7 +60,7 @@ class WireGatewayApiTest { } // Test endpoint is correctly authenticated - suspend fun ApplicationTestBuilder.authRoutine(path: String, body: JsonObject? = null, method: HttpMethod = HttpMethod.Post) { + suspend fun ApplicationTestBuilder.authRoutine(path: String, body: JsonObject? = null, method: HttpMethod = HttpMethod.Post, requireAdmin: Boolean = false) { // No body when authentication must happen before parsing the body // Unknown account @@ -81,17 +81,28 @@ class WireGatewayApiTest { basicAuth("exchange", "merchant-password") }.assertUnauthorized() + if (requireAdmin) { + // Not exchange account + client.request(path) { + this.method = method + if (body != null) jsonBody(body) + basicAuth("merchant", "merchant-password") + }.assertUnauthorized() + } + // Not exchange account client.request(path) { this.method = method if (body != null) jsonBody(body) - basicAuth("merchant", "merchant-password") + if (requireAdmin) + basicAuth("admin", "admin-password") + else basicAuth("merchant", "merchant-password") }.assertConflict().assertErr(TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE) } // Testing the POST /transfer call from the TWG API. @Test - fun transfer() = bankSetup { db -> + fun transfer() = bankSetup { _ -> val valid_req = json { "request_uid" to randHashCode() "amount" to "KUDOS:55" @@ -109,10 +120,7 @@ class WireGatewayApiTest { }.assertConflict().assertErr(TalerErrorCode.BANK_UNALLOWED_DEBIT) // Giving debt allowance and checking the OK case. - assert(db.bankAccountSetMaxDebt( - 2L, - TalerAmount(1000, 0, "KUDOS") - )) + setMaxDebt("exchange", TalerAmount("KUDOS:1000")) client.post("/accounts/exchange/taler-wire-gateway/transfer") { basicAuth("exchange", "exchange-password") jsonBody(valid_req) @@ -216,12 +224,7 @@ class WireGatewayApiTest { @Test fun historyIncoming() = bankSetup { db -> // Give Foo reasonable debt allowance: - assert( - db.bankAccountSetMaxDebt( - 1L, - TalerAmount(1000000, 0, "KUDOS") - ) - ) + setMaxDebt("merchant", TalerAmount("KUDOS:1000")) suspend fun HttpResponse.assertHistory(size: Int) { assertOk() @@ -390,13 +393,7 @@ class WireGatewayApiTest { */ @Test fun historyOutgoing() = bankSetup { db -> - // Give Bar reasonable debt allowance: - assert( - db.bankAccountSetMaxDebt( - 2L, - TalerAmount(1000000, 0, "KUDOS") - ) - ) + setMaxDebt("exchange", TalerAmount("KUDOS:1000000")) suspend fun HttpResponse.assertHistory(size: Int) { assertOk() @@ -505,41 +502,37 @@ class WireGatewayApiTest { // Testing the /admin/add-incoming call from the TWG API. @Test - fun addIncoming() = bankSetup { db -> + fun addIncoming() = bankSetup { _ -> val valid_req = json { "amount" to "KUDOS:44" "reserve_pub" to randEddsaPublicKey() "debit_account" to "payto://iban/MERCHANT-IBAN-XYZ" }; - authRoutine("/accounts/merchant/taler-wire-gateway/admin/add-incoming", valid_req) + authRoutine("/accounts/merchant/taler-wire-gateway/admin/add-incoming", valid_req, requireAdmin = true) // Checking exchange debt constraint. client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("exchange", "exchange-password") + basicAuth("admin", "admin-password") jsonBody(valid_req) }.assertConflict().assertErr(TalerErrorCode.BANK_UNALLOWED_DEBIT) // Giving debt allowance and checking the OK case. - assert(db.bankAccountSetMaxDebt( - 1L, - TalerAmount(1000, 0, "KUDOS") - )) - + setMaxDebt("merchant", TalerAmount("KUDOS:1000")) client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("exchange", "exchange-password") + basicAuth("admin", "admin-password") jsonBody(valid_req, deflate = true) }.assertOk() // Trigger conflict due to reused reserve_pub client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("exchange", "exchange-password") + basicAuth("admin", "admin-password") jsonBody(valid_req) }.assertConflict().assertErr(TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT) // Currency mismatch client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("exchange", "exchange-password") + basicAuth("admin", "admin-password") jsonBody( json(valid_req) { "amount" to "EUR:33" @@ -549,7 +542,7 @@ class WireGatewayApiTest { // Unknown account client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("exchange", "exchange-password") + basicAuth("admin", "admin-password") jsonBody( json(valid_req) { "reserve_pub" to randEddsaPublicKey() @@ -560,7 +553,7 @@ class WireGatewayApiTest { // Same account client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("exchange", "exchange-password") + basicAuth("admin", "admin-password") jsonBody( json(valid_req) { "reserve_pub" to randEddsaPublicKey() @@ -571,7 +564,7 @@ class WireGatewayApiTest { // Bad BASE32 reserve_pub client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("exchange", "exchange-password") + basicAuth("admin", "admin-password") jsonBody(json(valid_req) { "reserve_pub" to "I love chocolate" }) @@ -579,7 +572,7 @@ class WireGatewayApiTest { // Bad BASE32 len reserve_pub client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("exchange", "exchange-password") + basicAuth("admin", "admin-password") jsonBody(json(valid_req) { "reserve_pub" to randBase32Crockford(31) }) diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt @@ -83,6 +83,15 @@ fun dbSetup(lambda: suspend (Database) -> Unit) { setup() { db, _ -> lambda(db) } } +/* ----- Common actions ----- */ + +suspend fun ApplicationTestBuilder.setMaxDebt(account: String, maxDebt: TalerAmount) { + client.patch("/accounts/$account") { + basicAuth("admin", "admin-password") + jsonBody(json { "debit_threshold" to maxDebt }) + }.assertNoContent() +} + /* ----- Assert ----- */ fun HttpResponse.assertStatus(status: HttpStatusCode): HttpResponse {