commit 1e658ae784f94df9880db31732c539eeb930df4f
parent c31db1a6cb2adc62e0bd7a564c3b90e8ec3ae3f7
Author: Antoine A <>
Date: Fri, 1 Dec 2023 11:49:28 +0000
Restrict exchange account deletion when conversion is enabled
Diffstat:
7 files changed, 57 insertions(+), 19 deletions(-)
diff --git a/bank/conf/test_bonus.conf b/bank/conf/test_bonus.conf
@@ -1,6 +1,5 @@
[libeufin-bank]
CURRENCY = KUDOS
-DEFAULT_CUSTOMER_DEBT_LIMIT = KUDOS:0
DEFAULT_ADMIN_DEBT_LIMIT = KUDOS:10000
REGISTRATION_BONUS = KUDOS:100
ALLOW_REGISTRATION = yes
diff --git a/bank/conf/test_no_conversion.conf b/bank/conf/test_no_conversion.conf
@@ -0,0 +1,8 @@
+[libeufin-bank]
+CURRENCY = KUDOS
+DEFAULT_ADMIN_DEBT_LIMIT = KUDOS:10000
+ALLOW_REGISTRATION = yes
+ALLOW_ACCOUNT_DELETION = yes
+
+[libeufin-bankdb-postgres]
+CONFIG = postgresql:///libeufincheck
+\ No newline at end of file
diff --git a/bank/conf/test_restrict.conf b/bank/conf/test_restrict.conf
@@ -2,6 +2,8 @@
CURRENCY = KUDOS
DEFAULT_CUSTOMER_DEBT_LIMIT = KUDOS:100
DEFAULT_ADMIN_DEBT_LIMIT = KUDOS:10000
+allow_conversion = YES
+FIAT_CURRENCY = EUR
[libeufin-bankdb-postgres]
CONFIG = postgresql:///libeufincheck
\ 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
@@ -105,7 +105,6 @@ private fun Routing.coreBankTokenApi(db: Database) {
logger.debug("Creating token with days duration: ${tokenDuration.toDays()}")
creationTime.plus(tokenDuration)
} catch (e: Exception) {
- logger.error("Could not add token duration to current time: ${e.message}")
throw badRequest("Bad token duration: ${e.message}")
}
}
@@ -187,13 +186,17 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) {
requireAdmin = !ctx.allowAccountDeletion
) {
delete("/accounts/{USERNAME}") {
- // TODO prevent delection if exchange account if conversion is enabled
// Not deleting reserved names.
if (RESERVED_ACCOUNTS.contains(username))
throw conflict(
"Cannot delete reserved accounts",
TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT
)
+ if (username == "exchange" && ctx.allowConversion)
+ throw conflict(
+ "Cannot delete 'exchange' accounts when conversion is enabled",
+ TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT
+ )
when (db.account.delete(username)) {
AccountDeletionResult.UnknownAccount -> throw notFound(
diff --git a/bank/src/test/kotlin/ConversionApiTest.kt b/bank/src/test/kotlin/ConversionApiTest.kt
@@ -124,7 +124,7 @@ class ConversionApiTest {
}
@Test
- fun notImplemented() = bankSetup("test_restrict.conf") { _ ->
+ fun notImplemented() = bankSetup("test_no_conversion.conf") { _ ->
client.get("/conversion-info/cashin-rate")
.assertNotImplemented()
client.get("/conversion-info/cashout-rate")
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -164,7 +164,7 @@ class CoreBankTokenApiTest {
class CoreBankAccountsApiTest {
// Testing the account creation and its idempotency
@Test
- fun createAccountTest() = bankSetup { _ ->
+ fun create() = bankSetup { _ ->
val ibanPayto = genIbanPaytoUri()
val req = obj {
"username" to "foo"
@@ -222,7 +222,7 @@ class CoreBankAccountsApiTest {
// Test account created with bonus
@Test
- fun createAccountBonusTest() = bankSetup(conf = "test_bonus.conf") { _ ->
+ fun createBonus() = bankSetup(conf = "test_bonus.conf") { _ ->
val req = obj {
"username" to "foo"
"password" to "xyz"
@@ -255,7 +255,7 @@ class CoreBankAccountsApiTest {
// Test admin-only account creation
@Test
- fun createAccountRestrictedTest() = bankSetup(conf = "test_restrict.conf") { _ ->
+ fun createRestricted() = bankSetup(conf = "test_restrict.conf") { _ ->
authRoutine(HttpMethod.Post, "/accounts", requireAdmin = true)
client.post("/accounts") {
pwAuth("admin")
@@ -269,7 +269,7 @@ class CoreBankAccountsApiTest {
// DELETE /accounts/USERNAME
@Test
- fun deleteAccount() = bankSetup { _ ->
+ fun delete() = bankSetup { _ ->
// Unknown account
client.delete("/accounts/unknown") {
pwAuth("admin")
@@ -281,6 +281,8 @@ class CoreBankAccountsApiTest {
pwAuth("admin")
}.assertConflict(TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT)
}
+ client.deleteA("/accounts/exchange")
+ .assertConflict(TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT)
// successful deletion
client.post("/accounts") {
@@ -301,18 +303,33 @@ class CoreBankAccountsApiTest {
// fail to delete, due to a non-zero balance.
tx("customer", "KUDOS:1", "merchant")
- client.delete("/accounts/merchant") {
- pwAuth("admin")
- }.assertConflict(TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO)
+ client.deleteA("/accounts/merchant")
+ .assertConflict(TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO)
tx("merchant", "KUDOS:1", "customer")
- client.delete("/accounts/merchant") {
+ client.deleteA("/accounts/merchant")
+ .assertNoContent()
+ }
+
+ // Test admin-only account deletion
+ @Test
+ fun deleteRestricted() = bankSetup(conf = "test_restrict.conf") { _ ->
+ authRoutine(HttpMethod.Post, "/accounts", requireAdmin = true)
+ // Exchange is still restricted
+ client.delete("/accounts/exchange") {
pwAuth("admin")
- }.assertNoContent()
+ }.assertConflict(TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT)
+ }
+
+ // Test delete exchange account
+ @Test
+ fun deleteNoConversion() = bankSetup(conf = "test_no_conversion.conf") { _ ->
+ // Exchange is no longer restricted
+ client.deleteA("/accounts/exchange").assertNoContent()
}
// PATCH /accounts/USERNAME
@Test
- fun accountReconfig() = bankSetup { _ ->
+ fun reconfig() = bankSetup { _ ->
authRoutine(HttpMethod.Patch, "/accounts/merchant", withAdmin = true)
// Successful attempt now.
@@ -383,7 +400,7 @@ class CoreBankAccountsApiTest {
// PATCH /accounts/USERNAME/auth
@Test
- fun passwordChangeTest() = bankSetup { _ ->
+ fun passwordChange() = bankSetup { _ ->
authRoutine(HttpMethod.Patch, "/accounts/merchant/auth", withAdmin = true)
// Changing the password.
@@ -436,7 +453,7 @@ class CoreBankAccountsApiTest {
// GET /public-accounts and GET /accounts
@Test
- fun accountsListTest() = bankSetup { _ ->
+ fun list() = bankSetup(conf = "test_no_conversion.conf") { _ ->
authRoutine(HttpMethod.Get, "/accounts", requireAdmin = true)
// Remove default accounts
listOf("merchant", "exchange", "customer").forEach {
@@ -498,7 +515,7 @@ class CoreBankAccountsApiTest {
// GET /accounts/USERNAME
@Test
- fun getAccountTest() = bankSetup { _ ->
+ fun get() = bankSetup { _ ->
authRoutine(HttpMethod.Get, "/accounts/merchant", withAdmin = true)
// Check ok
client.getA("/accounts/merchant").assertOkJson<AccountData> {
@@ -510,7 +527,7 @@ class CoreBankAccountsApiTest {
class CoreBankTransactionsApiTest {
// GET /transactions
@Test
- fun testHistory() = bankSetup { _ ->
+ fun history() = bankSetup { _ ->
authRoutine(HttpMethod.Get, "/accounts/merchant/transactions")
historyRoutine<BankAccountTransactionsResponse>(
url = "/accounts/customer/transactions",
@@ -1248,7 +1265,7 @@ class CoreBankCashoutApiTest {
}
@Test
- fun notImplemented() = bankSetup("test_restrict.conf") { _ ->
+ fun notImplemented() = bankSetup("test_no_conversion.conf") { _ ->
client.get("/accounts/customer/cashouts")
.assertNotImplemented()
}
diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt
@@ -375,6 +375,14 @@ inline suspend fun HttpClient.patchA(url: String, builder: HttpRequestBuilder.()
}
}
+/** Auto auth delete request */
+inline suspend fun HttpClient.deleteA(url: String, builder: HttpRequestBuilder.() -> Unit = {}): HttpResponse {
+ return delete(url) {
+ pwAuth()
+ builder(this)
+ }
+}
+
fun HttpRequestBuilder.pwAuth(username: String? = null) {
if (username != null) {
basicAuth("$username", "$username-password")