summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2023-12-06 13:28:56 +0000
committerAntoine A <>2023-12-06 13:28:56 +0000
commitca8914cd6342babaaceec672fad2f076869b8201 (patch)
treef9f390a6b91ab405547475f4f0858fbf60de0cbe
parentc12426e1e485038637fa46163f0dacf97c3b59a1 (diff)
downloadlibeufin-ca8914cd6342babaaceec672fad2f076869b8201.tar.gz
libeufin-ca8914cd6342babaaceec672fad2f076869b8201.tar.bz2
libeufin-ca8914cd6342babaaceec672fad2f076869b8201.zip
Improve accounts API
-rw-r--r--API_CHANGES.md6
-rw-r--r--bank/conf/test.conf2
-rw-r--r--bank/conf/test_no_tan.conf1
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/Config.kt4
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/Constants.kt2
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt36
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/Main.kt34
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt34
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt41
-rw-r--r--bank/src/test/kotlin/CoreBankApiTest.kt71
-rw-r--r--bank/src/test/kotlin/helpers.kt2
-rw-r--r--contrib/bank.conf6
-rw-r--r--database-versioning/libeufin-bank-procedures.sql16
-rw-r--r--integration/test/IntegrationTest.kt2
-rw-r--r--util/src/main/kotlin/TalerErrorCode.kt8
15 files changed, 175 insertions, 90 deletions
diff --git a/API_CHANGES.md b/API_CHANGES.md
index 8e4c9735..8a83c38d 100644
--- a/API_CHANGES.md
+++ b/API_CHANGES.md
@@ -8,6 +8,12 @@ This files contains all the API changes for the current release:
- CREATE /accounts: new debit_threshold field similar to the one of PATH /accounts
- GET /config: new default_debit_threshold field for the default debt limit for newly created accounts
- GET /config: new supported_tan_channels field which lists all the TAN channels supported by the server
+- GET /config: new allow_edit_name and allow_edit_cashout_payto_uri fields for path authorisation
+- POST /accounts: rename challenge_contact_data to contact_data and internal_payto_uri to payto_uri
+- PATCH /accounts/USERNAME: add is_public, remove is_taler_exchange and rename challenge_contact_data to contact_data
+- GET /accounts: add payto_uri, is_public and is_taler_exchange
+- GET /accounts/USERNAME: add is_public and is_taler_exchange
+- GET /public-accounts: add is_taler_exchange and rename account_name to username
## bank cli
diff --git a/bank/conf/test.conf b/bank/conf/test.conf
index b5f9e93f..b4eb3953 100644
--- a/bank/conf/test.conf
+++ b/bank/conf/test.conf
@@ -3,6 +3,8 @@ DEFAULT_DEBT_LIMIT = KUDOS:100
SUGGESTED_WITHDRAWAL_EXCHANGE = https://exchange.example.com
ALLOW_REGISTRATION = yes
ALLOW_ACCOUNT_DELETION = yes
+ALLOW_EDIT_NAME = yes
+ALLOW_EDIT_CASHOUT_PAYTO_URI = yes
allow_conversion = YES
FIAT_CURRENCY = EUR
tan_sms = libeufin-tan-file.sh
diff --git a/bank/conf/test_no_tan.conf b/bank/conf/test_no_tan.conf
index 620cdbe4..52e824b2 100644
--- a/bank/conf/test_no_tan.conf
+++ b/bank/conf/test_no_tan.conf
@@ -5,6 +5,7 @@ allow_conversion = YES
FIAT_CURRENCY = EUR
ALLOW_REGISTRATION = yes
ALLOW_ACCOUNT_DELETION = yes
+ALLOW_EDIT_CASHOUT_PAYTO_URI = yes
[libeufin-bankdb-postgres]
CONFIG = postgresql:///libeufincheck
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
index 936944d4..c3f7db94 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Config.kt
@@ -37,6 +37,8 @@ data class BankConfig(
val regionalCurrencySpec: CurrencySpecification,
val allowRegistration: Boolean,
val allowAccountDeletion: Boolean,
+ val allowEditName: Boolean,
+ val allowEditCashout: Boolean,
val defaultDebtLimit: TalerAmount,
val registrationBonus: TalerAmount,
val suggestedWithdrawalExchange: String?,
@@ -115,6 +117,8 @@ fun TalerConfig.loadBankConfig(): BankConfig {
regionalCurrencySpec = currencySpecificationFor(regionalCurrency),
allowRegistration = lookupBoolean("libeufin-bank", "allow_registration") ?: false,
allowAccountDeletion = lookupBoolean("libeufin-bank", "allow_account_deletion") ?: false,
+ allowEditName = lookupBoolean("libeufin-bank", "allow_edit_name") ?: false,
+ allowEditCashout = lookupBoolean("libeufin-bank", "allow_edit_cashout_payto_uri") ?: false,
defaultDebtLimit = amount("libeufin-bank", "default_debt_limit", regionalCurrency) ?: TalerAmount(0, 0, regionalCurrency),
registrationBonus = amount("libeufin-bank", "registration_bonus", regionalCurrency) ?: TalerAmount(0, 0, regionalCurrency),
suggestedWithdrawalExchange = lookupString("libeufin-bank", "suggested_withdrawal_exchange"),
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Constants.kt b/bank/src/main/kotlin/tech/libeufin/bank/Constants.kt
index 385c9be0..b475aacd 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Constants.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Constants.kt
@@ -39,7 +39,7 @@ val RESERVED_ACCOUNTS = setOf("admin", "bank")
const val MAX_BODY_LENGTH: Long = 4 * 1024 // 4kB
// API version
-const val COREBANK_API_VERSION: String = "0:0:1"
+const val COREBANK_API_VERSION: String = "2:0: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:0: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 560ab0d2..938ab566 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -54,7 +54,9 @@ fun Routing.coreBankApi(db: Database, ctx: BankConfig) {
allow_registrations = ctx.allowRegistration,
allow_deletions = ctx.allowAccountDeletion,
default_debit_threshold = ctx.defaultDebtLimit,
- supported_tan_channels = ctx.tanChannels.keys
+ supported_tan_channels = ctx.tanChannels.keys,
+ allow_edit_name = ctx.allowEditName,
+ allow_edit_cashout_payto_uri = ctx.allowEditCashout
)
)
}
@@ -152,12 +154,13 @@ suspend fun createAccount(db: Database, ctx: BankConfig, req: RegisterAccountReq
)
- val internalPayto = req.internal_payto_uri ?: IbanPayTo(genIbanPaytoUri())
+ val internalPayto = req.payto_uri ?: req.internal_payto_uri ?: IbanPayTo(genIbanPaytoUri())
+ val contactData = req.contact_data ?: req.challenge_contact_data
val res = db.account.create(
login = req.username,
name = req.name,
- email = req.challenge_contact_data?.email,
- phone = req.challenge_contact_data?.phone,
+ email = contactData?.email,
+ phone = contactData?.phone,
cashoutPayto = req.cashout_payto_uri,
password = req.password,
internalPaytoUri = internalPayto,
@@ -173,22 +176,19 @@ suspend fun createAccount(db: Database, ctx: BankConfig, req: RegisterAccountReq
suspend fun patchAccount(db: Database, ctx: BankConfig, req: AccountReconfiguration, username: String, isAdmin: Boolean): AccountPatchResult {
req.debit_threshold?.run { ctx.checkRegionalCurrency(this) }
+ val contactData = req.contact_data ?: req.challenge_contact_data
- if ((req.is_taler_exchange ?: false) == true && username == "admin")
- throw conflict(
- "admin account cannot be an exchange",
- TalerErrorCode.BANK_PATCH_ADMIN_EXCHANGE
- )
-
- return db.account.reconfig(
+ return db.account.reconfig(
login = username,
name = req.name,
cashoutPayto = req.cashout_payto_uri,
- emailAddress = req.challenge_contact_data?.email,
- isTalerExchange = req.is_taler_exchange,
- phoneNumber = req.challenge_contact_data?.phone,
+ emailAddress = contactData?.email,
+ isPublic = req.is_public,
+ phoneNumber = contactData?.phone,
debtLimit = req.debit_threshold,
- isAdmin = isAdmin
+ isAdmin = isAdmin,
+ allowEditName = ctx.allowEditName,
+ allowEditCashout = ctx.allowEditCashout
)
}
@@ -255,10 +255,14 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) {
"Account '$username' not found",
TalerErrorCode.BANK_UNKNOWN_ACCOUNT
)
- AccountPatchResult.NonAdminLegalName -> throw conflict(
+ AccountPatchResult.NonAdminName -> throw conflict(
"non-admin user cannot change their legal name",
TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME
)
+ AccountPatchResult.NonAdminCashout -> throw conflict(
+ "non-admin user cannot change their cashout account",
+ TalerErrorCode.BANK_NON_ADMIN_PATCH_CASHOUT
+ )
AccountPatchResult.NonAdminDebtLimit -> throw conflict(
"non-admin user cannot change their debt limit",
TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index 715a46db..b9af5aeb 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -347,7 +347,11 @@ class EditAccount : CliktCommand(
help = "Legal name of the account owner"
)
private val exchange: Boolean? by option(
- help = "Is this account a taler exchange"
+ hidden = true
+ ).boolean()
+ private val is_public: Boolean? by option(
+ "--public",
+ help = "Make this account visible to anyone"
).boolean()
private val email: String? by option(help = "E-Mail address used for TAN transmission")
private val phone: String? by option(help = "Phone number used for TAN transmission")
@@ -363,7 +367,8 @@ class EditAccount : CliktCommand(
val req = AccountReconfiguration(
name = name,
is_taler_exchange = exchange,
- challenge_contact_data = ChallengeContactData(
+ is_public = is_public,
+ contact_data = ChallengeContactData(
email = email,
phone = phone,
),
@@ -375,10 +380,11 @@ class EditAccount : CliktCommand(
logger.info("Account '$username' edited")
AccountPatchResult.UnknownAccount ->
throw Exception("Account '$username' not found")
- AccountPatchResult.NonAdminLegalName ->
- throw Exception("non-admin user cannot change their legal name")
- AccountPatchResult.NonAdminDebtLimit ->
- throw Exception("non-admin user cannot change their debt limit")
+ AccountPatchResult.NonAdminName,
+ AccountPatchResult.NonAdminCashout,
+ AccountPatchResult.NonAdminDebtLimit -> {
+ // Unreachable as we edit account as admin
+ }
}
}
}
@@ -405,9 +411,16 @@ class CreateAccountOption: OptionGroup() {
).flag()
val email: String? by option(help = "E-Mail address used for TAN transmission")
val phone: String? by option(help = "Phone number used for TAN transmission")
- val cashout_payto_uri: IbanPayTo? by option(help = "Payto URI of a fiant account who receive cashout amount").convert { IbanPayTo(it) }
- val internal_payto_uri: IbanPayTo? by option(help = "Payto URI of this account").convert { IbanPayTo(it) }
- val debit_threshold: TalerAmount? by option(help = "Max debit allowed for this account").convert { TalerAmount(it) }
+ val cashout_payto_uri: IbanPayTo? by option(
+ help = "Payto URI of a fiant account who receive cashout amount"
+ ).convert { IbanPayTo(it) }
+ val internal_payto_uri: IbanPayTo? by option(hidden = true).convert { IbanPayTo(it) }
+ val payto_uri: IbanPayTo? by option(
+ help = "Payto URI of this account"
+ ).convert { IbanPayTo(it) }
+ val debit_threshold: TalerAmount? by option(
+ help = "Max debit allowed for this account")
+ .convert { TalerAmount(it) }
}
class CreateAccount : CliktCommand(
@@ -431,12 +444,13 @@ class CreateAccount : CliktCommand(
name = name,
is_public = is_public,
is_taler_exchange = exchange,
- challenge_contact_data = ChallengeContactData(
+ contact_data = ChallengeContactData(
email = email,
phone = phone,
),
cashout_payto_uri = cashout_payto_uri,
internal_payto_uri = internal_payto_uri,
+ payto_uri = payto_uri,
debit_threshold = debit_threshold
)
}
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
index 3b7cba75..f3beb697 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
@@ -113,12 +113,13 @@ data class RegisterAccountRequest(
val name: String,
val is_public: Boolean = false,
val is_taler_exchange: Boolean = false,
- val challenge_contact_data: ChallengeContactData? = null,
- // Fiat bank account where to send cashout amounts.
+ val contact_data: ChallengeContactData? = null,
val cashout_payto_uri: IbanPayTo? = null,
- // Bank account internal to Libeufin-Bank.
+ val payto_uri: IbanPayTo? = null,
+ val debit_threshold: TalerAmount? = null,
+ // TODO remove
val internal_payto_uri: IbanPayTo? = null,
- val debit_threshold: TalerAmount? = null
+ val challenge_contact_data: ChallengeContactData? = null,
)
@Serializable
@@ -131,11 +132,14 @@ data class RegisterAccountResponse(
*/
@Serializable
data class AccountReconfiguration(
- val challenge_contact_data: ChallengeContactData?,
- val cashout_payto_uri: IbanPayTo?,
- val name: String?,
- val is_taler_exchange: Boolean?,
- val debit_threshold: TalerAmount?
+ val contact_data: ChallengeContactData? = null,
+ val cashout_payto_uri: IbanPayTo? = null,
+ val name: String? = null,
+ val is_public: Boolean? = null,
+ val debit_threshold: TalerAmount? = null,
+ // TODO remove
+ val challenge_contact_data: ChallengeContactData? = null,
+ val is_taler_exchange: Boolean? = null,
)
/**
@@ -231,6 +235,8 @@ data class Config(
val allow_conversion: Boolean,
val allow_registrations: Boolean,
val allow_deletions: Boolean,
+ val allow_edit_name: Boolean,
+ val allow_edit_cashout_payto_uri: Boolean,
val default_debit_threshold: TalerAmount,
val supported_tan_channels: Set<TanChannel>
) {
@@ -276,8 +282,11 @@ data class Balance(
data class AccountMinimalData(
val username: String,
val name: String,
+ val payto_uri: String,
val balance: Balance,
- val debit_threshold: TalerAmount
+ val debit_threshold: TalerAmount,
+ val is_public: Boolean,
+ val is_taler_exchange: Boolean
)
/**
@@ -299,6 +308,8 @@ data class AccountData(
val debit_threshold: TalerAmount,
val contact_data: ChallengeContactData? = null,
val cashout_payto_uri: String? = null,
+ val is_public: Boolean,
+ val is_taler_exchange: Boolean
)
@Serializable
@@ -586,8 +597,11 @@ data class PublicAccountsResponse(
*/
@Serializable
data class PublicAccount(
+ val username: String,
val payto_uri: String,
val balance: Balance,
+ val is_taler_exchange: Boolean,
+ // TODO remove
val account_name: String
)
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
index 2cf85520..9520512d 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
@@ -196,7 +196,8 @@ class AccountDAO(private val db: Database) {
/** Result status of customer account patch */
enum class AccountPatchResult {
UnknownAccount,
- NonAdminLegalName,
+ NonAdminName,
+ NonAdminCashout,
NonAdminDebtLimit,
Success
}
@@ -208,25 +209,29 @@ class AccountDAO(private val db: Database) {
cashoutPayto: IbanPayTo?,
phoneNumber: String?,
emailAddress: String?,
- isTalerExchange: Boolean?,
+ isPublic: Boolean?,
debtLimit: TalerAmount?,
- isAdmin: Boolean
+ isAdmin: Boolean,
+ allowEditName: Boolean,
+ allowEditCashout: Boolean,
): AccountPatchResult = db.serializable { conn ->
+ println("$name $cashoutPayto $allowEditName $allowEditCashout")
val stmt = conn.prepareStatement("""
SELECT
out_not_found,
- out_legal_name_change,
+ out_name_change,
+ out_cashout_change,
out_debt_limit_change
- FROM account_reconfig(?, ?, ?, ?, ?, ?, (?, ?)::taler_amount, ?)
+ FROM account_reconfig(?, ?, ?, ?, ?, ?, (?, ?)::taler_amount, ?, ?, ?)
""")
stmt.setString(1, login)
stmt.setString(2, name)
stmt.setString(3, phoneNumber)
stmt.setString(4, emailAddress)
stmt.setString(5, cashoutPayto?.canonical)
- if (isTalerExchange == null)
+ if (isPublic == null)
stmt.setNull(6, Types.NULL)
- else stmt.setBoolean(6, isTalerExchange)
+ else stmt.setBoolean(6, isPublic)
if (debtLimit == null) {
stmt.setNull(7, Types.NULL)
stmt.setNull(8, Types.NULL)
@@ -235,11 +240,14 @@ class AccountDAO(private val db: Database) {
stmt.setInt(8, debtLimit.frac)
}
stmt.setBoolean(9, isAdmin)
+ stmt.setBoolean(10, allowEditName)
+ stmt.setBoolean(11, allowEditCashout)
stmt.executeQuery().use {
when {
!it.next() -> throw internalServerError("accountReconfig() returned nothing")
it.getBoolean("out_not_found") -> AccountPatchResult.UnknownAccount
- it.getBoolean("out_legal_name_change") -> AccountPatchResult.NonAdminLegalName
+ it.getBoolean("out_name_change") -> AccountPatchResult.NonAdminName
+ it.getBoolean("out_cashout_change") -> AccountPatchResult.NonAdminCashout
it.getBoolean("out_debt_limit_change") -> AccountPatchResult.NonAdminDebtLimit
else -> AccountPatchResult.Success
}
@@ -337,6 +345,8 @@ class AccountDAO(private val db: Database) {
,has_debt
,(max_debt).val AS max_debt_val
,(max_debt).frac AS max_debt_frac
+ ,is_public
+ ,is_taler_exchange
FROM customers
JOIN bank_accounts
ON customer_id=owning_customer_id
@@ -362,6 +372,8 @@ class AccountDAO(private val db: Database) {
}
),
debit_threshold = it.getAmount("max_debt", db.bankCurrency),
+ is_public = it.getBoolean("is_public"),
+ is_taler_exchange = it.getBoolean("is_taler_exchange")
)
}
}
@@ -377,7 +389,8 @@ class AccountDAO(private val db: Database) {
(balance).frac AS balance_frac,
has_debt,
internal_payto_uri,
- c.login
+ c.login
+ ,is_taler_exchange
FROM bank_accounts JOIN customers AS c
ON owning_customer_id = c.customer_id
WHERE is_public=true AND c.login LIKE ? AND
@@ -388,6 +401,7 @@ class AccountDAO(private val db: Database) {
}
) {
PublicAccount(
+ username = it.getString("login"),
account_name = it.getString("login"),
payto_uri = it.getString("internal_payto_uri"),
balance = Balance(
@@ -397,7 +411,8 @@ class AccountDAO(private val db: Database) {
} else {
CreditDebitInfo.credit
}
- )
+ ),
+ is_taler_exchange = it.getBoolean("is_taler_exchange")
)
}
@@ -415,6 +430,9 @@ class AccountDAO(private val db: Database) {
(b).has_debt AS balance_has_debt,
(max_debt).val as max_debt_val,
(max_debt).frac as max_debt_frac
+ ,is_public
+ ,is_taler_exchange
+ ,internal_payto_uri
FROM customers JOIN bank_accounts AS b
ON customer_id = b.owning_customer_id
WHERE name LIKE ? AND
@@ -436,6 +454,9 @@ class AccountDAO(private val db: Database) {
}
),
debit_threshold = it.getAmount("max_debt", db.bankCurrency),
+ is_public = it.getBoolean("is_public"),
+ is_taler_exchange = it.getBoolean("is_taler_exchange"),
+ payto_uri = it.getString("internal_payto_uri"),
)
}
} \ No newline at end of file
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt
index dedca301..f032a6d6 100644
--- a/bank/src/test/kotlin/CoreBankApiTest.kt
+++ b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -368,6 +368,18 @@ class CoreBankAccountsApiTest {
client.deleteA("/accounts/exchange").assertNoContent()
}
+ suspend fun ApplicationTestBuilder.checkAdminOnly(req: JsonElement, error: TalerErrorCode) {
+ // Checking ordinary user doesn't get to patch
+ client.patchA("/accounts/merchant") {
+ json(req)
+ }.assertConflict(error)
+ // Finally checking that admin does get to patch
+ client.patch("/accounts/merchant") {
+ pwAuth("admin")
+ json(req)
+ }.assertNoContent()
+ }
+
// PATCH /accounts/USERNAME
@Test
fun reconfig() = bankSetup { _ ->
@@ -377,11 +389,12 @@ class CoreBankAccountsApiTest {
val cashout = IbanPayTo(genIbanPaytoUri())
val req = obj {
"cashout_payto_uri" to cashout.canonical
- "challenge_contact_data" to obj {
+ "contact_data" to obj {
"phone" to "+99"
"email" to "foo@example.com"
}
- "is_taler_exchange" to true
+ "name" to "Roger"
+ "is_public" to true
}
client.patchA("/accounts/merchant") {
json(req)
@@ -390,36 +403,11 @@ class CoreBankAccountsApiTest {
client.patchA("/accounts/merchant") {
json(req)
}.assertNoContent()
-
- suspend fun checkAdminOnly(req: JsonElement, error: TalerErrorCode) {
- // Checking ordinary user doesn't get to patch
- client.patchA("/accounts/merchant") {
- json(req)
- }.assertConflict(error)
- // Finally checking that admin does get to patch
- client.patch("/accounts/merchant") {
- pwAuth("admin")
- json(req)
- }.assertNoContent()
- }
-
- checkAdminOnly(
- obj(req) { "name" to "Another Foo" },
- TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME
- )
+
checkAdminOnly(
obj(req) { "debit_threshold" to "KUDOS:100" },
TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT
)
-
- // Check admin account cannot be exchange
- client.patchA("/accounts/admin") {
- json { "is_taler_exchange" to true }
- }.assertConflict(TalerErrorCode.BANK_PATCH_ADMIN_EXCHANGE)
- // But we can change its debt limit
- client.patchA("/accounts/admin") {
- json { "debit_threshold" to "KUDOS:100" }
- }.assertNoContent()
// Check currency
client.patch("/accounts/merchant") {
@@ -428,17 +416,30 @@ class CoreBankAccountsApiTest {
}.assertBadRequest()
// Check patch
- client.get("/accounts/merchant") {
- pwAuth("admin")
- }.assertOkJson<AccountData> { obj ->
- assertEquals("Another Foo", obj.name)
+ client.getA("/accounts/merchant").assertOkJson<AccountData> { obj ->
+ assertEquals("Roger", obj.name)
assertEquals(cashout.canonical, obj.cashout_payto_uri)
assertEquals("+99", obj.contact_data?.phone)
assertEquals("foo@example.com", obj.contact_data?.email)
assertEquals(TalerAmount("KUDOS:100"), obj.debit_threshold)
+ assert(obj.is_public)
+ assert(!obj.is_taler_exchange)
}
}
+ // Test admin-only account patch
+ @Test
+ fun patchRestricted() = bankSetup(conf = "test_restrict.conf") { _ ->
+ checkAdminOnly(
+ obj { "name" to "Another Foo" },
+ TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME
+ )
+ checkAdminOnly(
+ obj { "cashout_payto_uri" to genIbanPaytoUri() },
+ TalerErrorCode.BANK_NON_ADMIN_PATCH_CASHOUT
+ )
+ }
+
// PATCH /accounts/USERNAME/auth
@Test
fun passwordChange() = bankSetup { _ ->
@@ -936,7 +937,7 @@ class CoreBankCashoutApiTest {
}.assertConflict(TalerErrorCode.BANK_MISSING_TAN_INFO)
client.patchA("/accounts/customer") {
json {
- "challenge_contact_data" to obj {
+ "contact_data" to obj {
"phone" to "+99"
"email" to "foo@example.com"
}
@@ -1007,7 +1008,7 @@ class CoreBankCashoutApiTest {
// POST /accounts/{USERNAME}/cashouts
@Test
- fun create_no_tan() = bankSetup("test_no_tan.conf") { _ ->
+ fun createNoTan() = bankSetup("test_no_tan.conf") { _ ->
val req = obj {
"request_uid" to randShortHashCode()
"amount_debit" to "KUDOS:1"
@@ -1093,7 +1094,7 @@ class CoreBankCashoutApiTest {
client.patchA("/accounts/customer") {
json {
- "challenge_contact_data" to obj {
+ "contact_data" to obj {
"phone" to "+99"
}
}
diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt
index 00830bb6..cc02e67d 100644
--- a/bank/src/test/kotlin/helpers.kt
+++ b/bank/src/test/kotlin/helpers.kt
@@ -236,7 +236,7 @@ suspend fun ApplicationTestBuilder.fillCashoutInfo(account: String) {
client.patchA("/accounts/$account") {
json {
"cashout_payto_uri" to unknownPayto
- "challenge_contact_data" to obj {
+ "contact_data" to obj {
"phone" to "+99"
}
}
diff --git a/contrib/bank.conf b/contrib/bank.conf
index 5b0ef359..7fe0c853 100644
--- a/contrib/bank.conf
+++ b/contrib/bank.conf
@@ -15,6 +15,12 @@ CURRENCY = KUDOS
# Allow an account to delete itself
# ALLOW_ACCOUNT_DELETION = no
+# Allow accounts to edit their name
+# ALLOW_EDIT_NAME = no
+
+# Allow accounts to edit their cashout account
+# ALLOW_EDIT_CASHOUT_PAYTO_URI = no
+
# Enable regional currency conversion
# ALLOW_CONVERSION = no
diff --git a/database-versioning/libeufin-bank-procedures.sql b/database-versioning/libeufin-bank-procedures.sql
index d9ed9f18..455d09b2 100644
--- a/database-versioning/libeufin-bank-procedures.sql
+++ b/database-versioning/libeufin-bank-procedures.sql
@@ -123,11 +123,14 @@ CREATE OR REPLACE FUNCTION account_reconfig(
IN in_phone TEXT,
IN in_email TEXT,
IN in_cashout_payto TEXT,
- IN in_is_taler_exchange BOOLEAN,
+ IN in_is_public BOOLEAN,
IN in_max_debt taler_amount,
IN in_is_admin BOOLEAN,
+ IN in_allow_name BOOLEAN,
+ IN in_allow_cashout BOOLEAN,
OUT out_not_found BOOLEAN,
- OUT out_legal_name_change BOOLEAN,
+ OUT out_name_change BOOLEAN,
+ OUT out_cashout_change BOOLEAN,
OUT out_debt_limit_change BOOLEAN
)
LANGUAGE plpgsql AS $$
@@ -140,9 +143,10 @@ END IF;
-- Get user ID and check reconfig rights
SELECT
customer_id,
- in_name IS NOT NULL AND name != in_name AND NOT in_is_admin,
+ in_name IS NOT NULL AND name != in_name AND NOT in_is_admin AND NOT in_allow_name,
+ cashout_payto IS DISTINCT FROM in_cashout_payto AND NOT in_is_admin AND NOT in_allow_cashout,
in_max_debt IS NOT NULL AND max_debt != in_max_debt AND NOT in_is_admin
- INTO my_customer_id, out_legal_name_change, out_debt_limit_change
+ INTO my_customer_id, out_name_change, out_cashout_change, out_debt_limit_change
FROM customers
JOIN bank_accounts
ON customer_id=owning_customer_id
@@ -150,13 +154,13 @@ SELECT
IF NOT FOUND THEN
out_not_found=TRUE;
RETURN;
-ELSIF out_legal_name_change OR out_debt_limit_change THEN
+ELSIF out_name_change OR out_cashout_change OR out_debt_limit_change THEN
RETURN;
END IF;
-- Update bank info
UPDATE bank_accounts SET
- is_taler_exchange = COALESCE(in_is_taler_exchange, is_taler_exchange),
+ is_public = COALESCE(in_is_public, is_public),
max_debt = COALESCE(in_max_debt, max_debt)
WHERE owning_customer_id = my_customer_id;
-- Update customer info
diff --git a/integration/test/IntegrationTest.kt b/integration/test/IntegrationTest.kt
index 5820a205..f32d2a75 100644
--- a/integration/test/IntegrationTest.kt
+++ b/integration/test/IntegrationTest.kt
@@ -106,7 +106,7 @@ class IntegrationTest {
"internal_payto_uri" to userPayTo
"cashout_payto_uri" to fiatPayTo
"debit_threshold" to "KUDOS:100"
- "challenge_contact_data" to obj {
+ "contact_data" to obj {
"phone" to "+99"
}
}
diff --git a/util/src/main/kotlin/TalerErrorCode.kt b/util/src/main/kotlin/TalerErrorCode.kt
index 70f08e34..ff9dce29 100644
--- a/util/src/main/kotlin/TalerErrorCode.kt
+++ b/util/src/main/kotlin/TalerErrorCode.kt
@@ -3474,6 +3474,14 @@ enum class TalerErrorCode(val code: Int) {
/**
+ * A non-admin user has tried to change their cashout 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_NON_ADMIN_PATCH_CASHOUT(5140),
+
+
+ /**
* 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).