diff options
author | Antoine A <> | 2023-12-14 12:42:30 +0000 |
---|---|---|
committer | Antoine A <> | 2023-12-14 12:42:30 +0000 |
commit | 165c0bee958d263522c07b658693d0d55fe7f3ac (patch) | |
tree | 11869e2c621fa9205f42c4e29d78e117895a53b4 | |
parent | d2021a25b65e8716545221152a45cd079e15d8c6 (diff) | |
download | libeufin-165c0bee958d263522c07b658693d0d55fe7f3ac.tar.gz libeufin-165c0bee958d263522c07b658693d0d55fe7f3ac.tar.bz2 libeufin-165c0bee958d263522c07b658693d0d55fe7f3ac.zip |
Prohibit manual transactions to admin and simplify procedures
-rw-r--r-- | API_CHANGES.md | 1 | ||||
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/Constants.kt | 2 | ||||
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt | 4 | ||||
-rw-r--r-- | bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt | 4 | ||||
-rw-r--r-- | bank/src/test/kotlin/AmountTest.kt | 4 | ||||
-rw-r--r-- | bank/src/test/kotlin/CoreBankApiTest.kt | 9 | ||||
-rw-r--r-- | database-versioning/libeufin-bank-procedures.sql | 85 | ||||
-rw-r--r-- | util/src/main/kotlin/TalerErrorCode.kt | 8 |
8 files changed, 55 insertions, 62 deletions
diff --git a/API_CHANGES.md b/API_CHANGES.md index a4fee024..08259cf5 100644 --- a/API_CHANGES.md +++ b/API_CHANGES.md @@ -16,6 +16,7 @@ This files contains all the API changes for the current release: - GET /public-accounts: add is_taler_exchange and rename account_name to username - PATCH /accounts: fix PATCH semantic - PATCH /accounts: restrict PATCH contact_data to admin +- POST /accounts/USERNAME/transactions: prohibit transaction to admin account ## bank cli diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Constants.kt b/bank/src/main/kotlin/tech/libeufin/bank/Constants.kt index 55f8ebfc..d2565cb1 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/Constants.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/Constants.kt @@ -42,7 +42,7 @@ const val MAX_BODY_LENGTH: Long = 4 * 1024 // 4kB const val MIN_VERSION: Int = 14 // API version -const val COREBANK_API_VERSION: String = "2:2:2" +const val COREBANK_API_VERSION: String = "2:3:2" const val CONVERSION_API_VERSION: String = "0:0:0" const val INTEGRATION_API_VERSION: String = "1:0:1" const val WIRE_GATEWAY_API_VERSION: String = "0:1:0"
\ No newline at end of file diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt index 248a8a83..64d27490 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt @@ -371,6 +371,10 @@ private fun Routing.coreBankTransactionsApi(db: Database, ctx: BankConfig) { "Creditor account was not found", TalerErrorCode.BANK_UNKNOWN_CREDITOR ) + is BankTransactionResult.AdminCreditor -> throw conflict( + "Cannot transfer money to admin account", + TalerErrorCode.BANK_ADMIN_CREDITOR + ) is BankTransactionResult.BalanceInsufficient -> throw conflict( "Insufficient funds", TalerErrorCode.BANK_UNALLOWED_DEBIT diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt index e90a25ff..df50a1a5 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt @@ -33,10 +33,10 @@ class TransactionDAO(private val db: Database) { sealed class BankTransactionResult { data class Success(val id: Long): BankTransactionResult() object UnknownCreditor: BankTransactionResult() + object AdminCreditor: BankTransactionResult() object UnknownDebtor: BankTransactionResult() object BothPartySame: BankTransactionResult() object BalanceInsufficient: BankTransactionResult() - } /** Create a new transaction */ @@ -61,6 +61,7 @@ class TransactionDAO(private val db: Database) { ,out_debit_row_id ,out_creditor_is_exchange ,out_debtor_is_exchange + ,out_creditor_admin FROM bank_transaction(?,?,?,(?,?)::taler_amount,?) """ ) @@ -77,6 +78,7 @@ class TransactionDAO(private val db: Database) { it.getBoolean("out_debtor_not_found") -> BankTransactionResult.UnknownDebtor it.getBoolean("out_same_account") -> BankTransactionResult.BothPartySame it.getBoolean("out_balance_insufficient") -> BankTransactionResult.BalanceInsufficient + it.getBoolean("out_creditor_admin") -> BankTransactionResult.AdminCreditor else -> { val creditAccountId = it.getLong("out_credit_bank_account_id") val creditRowId = it.getLong("out_credit_row_id") diff --git a/bank/src/test/kotlin/AmountTest.kt b/bank/src/test/kotlin/AmountTest.kt index d4aacc7a..c265db5a 100644 --- a/bank/src/test/kotlin/AmountTest.kt +++ b/bank/src/test/kotlin/AmountTest.kt @@ -32,7 +32,7 @@ class AmountTest { @Test fun computationTest() = bankSetup { db -> val conn = db.dbPool.getConnection().unwrap(PgConnection::class.java) - conn.execSQLUpdate("UPDATE libeufin_bank.bank_accounts SET balance.val = 100000 WHERE internal_payto_uri = '$exchangePayto'") + conn.execSQLUpdate("UPDATE libeufin_bank.bank_accounts SET balance.val = 100000 WHERE internal_payto_uri = '$customerPayto'") val stmt = conn.prepareStatement(""" UPDATE libeufin_bank.bank_accounts SET balance = (?, ?)::taler_amount @@ -50,7 +50,7 @@ class AmountTest { // Check bank transaction stmt.executeUpdate() val txRes = db.transaction.create( - creditAccountPayto = exchangePayto, + creditAccountPayto = customerPayto, debitAccountUsername = "merchant", subject = "test", amount = due, diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt index c9328f90..abac0acd 100644 --- a/bank/src/test/kotlin/CoreBankApiTest.kt +++ b/bank/src/test/kotlin/CoreBankApiTest.kt @@ -679,7 +679,7 @@ class CoreBankTransactionsApiTest { // POST /transactions @Test - fun create() = bankSetup { _ -> + fun create() = bankSetup { db -> authRoutine(HttpMethod.Post, "/accounts/merchant/transactions") val valid_req = obj { @@ -754,6 +754,13 @@ class CoreBankTransactionsApiTest { "payto_uri" to "$merchantPayto?message=payout" } }.assertConflict(TalerErrorCode.BANK_SAME_ACCOUNT) + // Transaction to admin + val adminPayto = db.account.bankInfo("admin")!!.internalPaytoUri + client.postA("/accounts/merchant/transactions") { + json(valid_req) { + "payto_uri" to "$adminPayto?message=payout" + } + }.assertConflict(TalerErrorCode.BANK_ADMIN_CREDITOR) // Init state assertBalance("merchant", "+KUDOS:0") diff --git a/database-versioning/libeufin-bank-procedures.sql b/database-versioning/libeufin-bank-procedures.sql index ea493857..6714a656 100644 --- a/database-versioning/libeufin-bank-procedures.sql +++ b/database-versioning/libeufin-bank-procedures.sql @@ -442,6 +442,7 @@ CREATE FUNCTION bank_transaction( OUT out_debtor_not_found BOOLEAN, OUT out_same_account BOOLEAN, OUT out_balance_insufficient BOOLEAN, + OUT out_creditor_admin BOOLEAN, -- Success return OUT out_credit_bank_account_id BIGINT, OUT out_debit_bank_account_id BIGINT, @@ -452,41 +453,39 @@ CREATE FUNCTION bank_transaction( ) LANGUAGE plpgsql AS $$ BEGIN --- Find credit bank account id -SELECT bank_account_id - INTO out_credit_bank_account_id +-- Find credit bank account id and check it's not admin +SELECT bank_account_id, is_taler_exchange, login='admin' + INTO out_credit_bank_account_id, out_creditor_is_exchange, out_creditor_admin FROM bank_accounts + JOIN customers ON customer_id=owning_customer_id WHERE internal_payto_uri = in_credit_account_payto; IF NOT FOUND THEN out_creditor_not_found=TRUE; RETURN; +ELSIF out_creditor_admin THEN + RETURN; END IF; --- Find debit bank account id -SELECT bank_account_id - INTO out_debit_bank_account_id +-- Find debit bank account id and check it's a different account +SELECT bank_account_id, is_taler_exchange, out_credit_bank_account_id=bank_account_id + INTO out_debit_bank_account_id, out_debtor_is_exchange, out_same_account FROM bank_accounts - JOIN customers - ON customer_id=owning_customer_id + JOIN customers ON customer_id=owning_customer_id WHERE login = in_debit_account_username; IF NOT FOUND THEN out_debtor_not_found=TRUE; RETURN; +ELSIF out_same_account THEN + RETURN; END IF; -- Perform bank transfer SELECT transfer.out_balance_insufficient, transfer.out_credit_row_id, - transfer.out_debit_row_id, - transfer.out_same_account, - transfer.out_creditor_is_exchange, - transfer.out_debtor_is_exchange + transfer.out_debit_row_id INTO out_balance_insufficient, out_credit_row_id, - out_debit_row_id, - out_same_account, - out_creditor_is_exchange, - out_debtor_is_exchange + out_debit_row_id FROM bank_wire_transfer( out_credit_bank_account_id, out_debit_bank_account_id, @@ -497,9 +496,6 @@ SELECT NULL, NULL ) as transfer; -IF out_balance_insufficient THEN - RETURN; -END IF; END $$; COMMENT ON FUNCTION bank_transaction IS 'Create a bank transaction'; @@ -733,15 +729,10 @@ CREATE FUNCTION bank_wire_transfer( IN in_payment_information_id TEXT, IN in_end_to_end_id TEXT, -- Error status - OUT out_same_account BOOLEAN, - OUT out_debtor_not_found BOOLEAN, - OUT out_creditor_not_found BOOLEAN, OUT out_balance_insufficient BOOLEAN, -- Success return OUT out_credit_row_id BIGINT, - OUT out_debit_row_id BIGINT, - OUT out_creditor_is_exchange BOOLEAN, - OUT out_debtor_is_exchange BOOLEAN + OUT out_debit_row_id BIGINT ) LANGUAGE plpgsql AS $$ DECLARE @@ -760,57 +751,39 @@ new_debtor_balance_ok BOOLEAN; new_creditor_balance taler_amount; will_debtor_have_debt BOOLEAN; will_creditor_have_debt BOOLEAN; -new_debit_row_id BIGINT; -new_credit_row_id BIGINT; BEGIN - -IF in_creditor_account_id=in_debtor_account_id THEN - out_same_account=TRUE; - RETURN; -END IF; -out_same_account=FALSE; - --- check debtor exists. +-- Retreive debtor info SELECT has_debt, (balance).val, (balance).frac, (max_debt).val, (max_debt).frac, - internal_payto_uri, customers.name, - is_taler_exchange + internal_payto_uri, customers.name INTO debtor_has_debt, debtor_balance.val, debtor_balance.frac, debtor_max_debt.val, debtor_max_debt.frac, - debtor_payto_uri, debtor_name, - out_debtor_is_exchange + debtor_payto_uri, debtor_name FROM bank_accounts - JOIN customers ON (bank_accounts.owning_customer_id = customers.customer_id) + JOIN customers ON customer_id=owning_customer_id WHERE bank_account_id=in_debtor_account_id; IF NOT FOUND THEN - out_debtor_not_found=TRUE; - RETURN; + RAISE EXCEPTION 'fuck debtor'; END IF; -out_debtor_not_found=FALSE; --- check creditor exists. Future versions may skip this --- due to creditors being hosted at other banks. +-- Retreive creditor info SELECT has_debt, (balance).val, (balance).frac, - internal_payto_uri, customers.name, - is_taler_exchange + internal_payto_uri, customers.name INTO creditor_has_debt, creditor_balance.val, creditor_balance.frac, - creditor_payto_uri, creditor_name, - out_creditor_is_exchange + creditor_payto_uri, creditor_name FROM bank_accounts - JOIN customers ON (bank_accounts.owning_customer_id = customers.customer_id) + JOIN customers ON customer_id=owning_customer_id WHERE bank_account_id=in_creditor_account_id; IF NOT FOUND THEN - out_creditor_not_found=TRUE; - RETURN; + RAISE EXCEPTION 'fuck creditor %', in_creditor_account_id; END IF; -out_creditor_not_found=FALSE; -- DEBTOR SIDE -- check debtor has enough funds. @@ -918,8 +891,7 @@ VALUES ( in_end_to_end_id, 'debit', in_debtor_account_id -) RETURNING bank_transaction_id INTO new_debit_row_id; -out_debit_row_id=new_debit_row_id; +) RETURNING bank_transaction_id INTO out_debit_row_id; -- debtor side: INSERT INTO bank_account_transactions ( @@ -949,8 +921,7 @@ VALUES ( in_end_to_end_id, -- does this interest the receiving party? 'credit', in_creditor_account_id -) RETURNING bank_transaction_id INTO new_credit_row_id; -out_credit_row_id=new_credit_row_id; +) RETURNING bank_transaction_id INTO out_credit_row_id; -- checks and balances set up, now update bank accounts. UPDATE bank_accounts diff --git a/util/src/main/kotlin/TalerErrorCode.kt b/util/src/main/kotlin/TalerErrorCode.kt index d7f11abd..8638dfc2 100644 --- a/util/src/main/kotlin/TalerErrorCode.kt +++ b/util/src/main/kotlin/TalerErrorCode.kt @@ -3490,6 +3490,14 @@ enum class TalerErrorCode(val code: Int) { /** + * The client tried to create a transaction that credit the admin account. + * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409). + * (A value of 0 indicates that the error is generated client-side). + */ + BANK_ADMIN_CREDITOR(5142), + + + /** * The sync service failed find the account in its database. * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404). * (A value of 0 indicates that the error is generated client-side). |