summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2023-12-14 12:42:30 +0000
committerAntoine A <>2023-12-14 12:42:30 +0000
commit165c0bee958d263522c07b658693d0d55fe7f3ac (patch)
tree11869e2c621fa9205f42c4e29d78e117895a53b4
parentd2021a25b65e8716545221152a45cd079e15d8c6 (diff)
downloadlibeufin-165c0bee958d263522c07b658693d0d55fe7f3ac.tar.gz
libeufin-165c0bee958d263522c07b658693d0d55fe7f3ac.tar.bz2
libeufin-165c0bee958d263522c07b658693d0d55fe7f3ac.zip
Prohibit manual transactions to admin and simplify procedures
-rw-r--r--API_CHANGES.md1
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/Constants.kt2
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt4
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt4
-rw-r--r--bank/src/test/kotlin/AmountTest.kt4
-rw-r--r--bank/src/test/kotlin/CoreBankApiTest.kt9
-rw-r--r--database-versioning/libeufin-bank-procedures.sql85
-rw-r--r--util/src/main/kotlin/TalerErrorCode.kt8
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).