diff options
author | Antoine A <> | 2023-12-19 15:58:48 +0000 |
---|---|---|
committer | Antoine A <> | 2023-12-19 16:09:13 +0000 |
commit | cf1456b82766a7c13338a59c84f6d8b1f38e9ad9 (patch) | |
tree | 684a81f8ac23980962034a1ea993d29588ea90e6 | |
parent | 14941dc6d049dd24ac9eba76561cf01d0002f746 (diff) | |
download | libeufin-cf1456b82766a7c13338a59c84f6d8b1f38e9ad9.tar.gz libeufin-cf1456b82766a7c13338a59c84f6d8b1f38e9ad9.tar.bz2 libeufin-cf1456b82766a7c13338a59c84f6d8b1f38e9ad9.zip |
Add tan_channel fields to bank accounts
7 files changed, 52 insertions, 9 deletions
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt index 9966edf5..fc344a80 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt @@ -205,6 +205,7 @@ suspend fun patchAccount(db: Database, ctx: BankConfig, req: AccountReconfigurat cashoutPayto = req.cashout_payto_uri, email = contactData?.email ?: Option.None, phone = contactData?.phone ?: Option.None, + tan_channel = req.tan_channel, isPublic = req.is_public, debtLimit = req.debit_threshold, isAdmin = isAdmin, @@ -267,7 +268,10 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { auth(db, TokenScope.readwrite, allowAdmin = true) { patch("/accounts/{USERNAME}") { val req = call.receive<AccountReconfiguration>() - when (patchAccount(db, ctx, req, username, isAdmin)) { + val res = patchAccount(db, ctx, req, username, isAdmin) + println(req) + println(res) + when (res) { AccountPatchResult.Success -> call.respond(HttpStatusCode.NoContent) AccountPatchResult.UnknownAccount -> throw unknownAccount(username) AccountPatchResult.NonAdminName -> throw conflict( @@ -286,6 +290,10 @@ private fun Routing.coreBankAccountsApi(db: Database, ctx: BankConfig) { "non-admin user cannot change their contact info", TalerErrorCode.BANK_NON_ADMIN_PATCH_CONTACT ) + AccountPatchResult.MissingTanInfo -> throw conflict( + "missing info for tan channel ${req.tan_channel.get()}", + TalerErrorCode.BANK_MISSING_TAN_INFO + ) } } patch("/accounts/{USERNAME}/auth") { diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt index 908c3234..154fcf9b 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt @@ -364,6 +364,7 @@ class EditAccount : CliktCommand( ).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") + private val tan_channel: String? by option(help = "which channel TAN challenges should be sent to") private val cashout_payto_uri: IbanPayTo? by option(help = "Payto URI of a fiant account who receive cashout amount").convert { IbanPayTo(it) } private val debit_threshold: TalerAmount? by option(help = "Max debit allowed for this account").convert { TalerAmount(it) } @@ -390,6 +391,8 @@ class EditAccount : CliktCommand( logger.info("Account '$username' edited") AccountPatchResult.UnknownAccount -> throw Exception("Account '$username' not found") + AccountPatchResult.MissingTanInfo -> + throw Exception("missing info for tan channel ${req.tan_channel.get()}") AccountPatchResult.NonAdminName, AccountPatchResult.NonAdminCashout, AccountPatchResult.NonAdminDebtLimit, diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt index 72633c73..61621847 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt @@ -176,6 +176,7 @@ data class AccountReconfiguration( val name: String? = null, val is_public: Boolean? = null, val debit_threshold: TalerAmount? = null, + val tan_channel: Option<TanChannel?> = Option.None, // TODO remove val challenge_contact_data: ChallengeContactData? = null, val is_taler_exchange: Boolean? = null, @@ -347,6 +348,7 @@ data class AccountData( val debit_threshold: TalerAmount, val contact_data: ChallengeContactData? = null, val cashout_payto_uri: String? = null, + val tan_channel: TanChannel? = null, val is_public: Boolean, val is_taler_exchange: Boolean ) 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 276e5e56..830d5bb0 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt @@ -92,7 +92,8 @@ class AccountDAO(private val db: Database) { ,email ,phone ,cashout_payto - ) VALUES (?, ?, ?, ?, ?, ?) + ,tan_channel + ) VALUES (?, ?, ?, ?, ?, ?, NULL) RETURNING customer_id """ ).run { @@ -200,6 +201,7 @@ class AccountDAO(private val db: Database) { NonAdminCashout, NonAdminDebtLimit, NonAdminContact, + MissingTanInfo, Success } @@ -210,6 +212,7 @@ class AccountDAO(private val db: Database) { cashoutPayto: Option<IbanPayTo?>, phone: Option<String?>, email: Option<String?>, + tan_channel: Option<TanChannel?>, isPublic: Boolean?, debtLimit: TalerAmount?, isAdmin: Boolean, @@ -226,11 +229,16 @@ class AccountDAO(private val db: Database) { val customer_id = conn.prepareStatement(""" SELECT customer_id - ,(${ if (checkName) "name != ? " else "false" }) as name_change + ,(${ if (checkName) "name != ?" else "false" }) as name_change ,(${ if (checkCashout) "cashout_payto IS DISTINCT FROM ?" else "false" }) as cashout_change ,(${ if (checkDebtLimit) "max_debt != (?, ?)::taler_amount" else "false" }) as debt_limit_change - ,(${ if (checkPhone) "phone IS DISTINCT FROM ?" else "false" }) as phone_change - ,(${ if (checkEmail) "email IS DISTINCT FROM ?" else "false" }) as email_change + ,(${ if (checkPhone) "phone IS DISTINCT FROM ?" else "false" }) as phone_change + ,(${ if (checkEmail) "email IS DISTINCT FROM ?" else "false" }) as email_change + ,(${ when (tan_channel.get()) { + null -> "false" + TanChannel.sms -> if (phone.get() != null) "false" else "phone IS NULL" + TanChannel.email -> if (email.get() != null) "false" else "email IS NULL" + }}) as missing_tan_info FROM customers JOIN bank_accounts ON customer_id=owning_customer_id @@ -262,6 +270,7 @@ class AccountDAO(private val db: Database) { it.getBoolean("debt_limit_change") -> return@transaction AccountPatchResult.NonAdminDebtLimit it.getBoolean("phone_change") -> return@transaction AccountPatchResult.NonAdminContact it.getBoolean("email_change") -> return@transaction AccountPatchResult.NonAdminContact + it.getBoolean("missing_tan_info") -> return@transaction AccountPatchResult.MissingTanInfo else -> it.getLong("customer_id") } } @@ -289,6 +298,7 @@ class AccountDAO(private val db: Database) { cashoutPayto.some { yield("cashout_payto=?") } phone.some { yield("phone=?") } email.some { yield("email=?") } + tan_channel.some { yield("tan_channel=?::tan_enum") } name?.let { yield("name=?") } }, "WHERE customer_id = ?", @@ -296,6 +306,7 @@ class AccountDAO(private val db: Database) { cashoutPayto.some { yield(it?.canonical) } phone.some { yield(it) } email.some { yield(it) } + tan_channel.some { yield(it?.name) } name?.let { yield(it) } yield(customer_id) } @@ -388,6 +399,7 @@ class AccountDAO(private val db: Database) { name ,email ,phone + ,tan_channel ,cashout_payto ,internal_payto_uri ,(balance).val AS balance_val @@ -398,7 +410,7 @@ class AccountDAO(private val db: Database) { ,is_public ,is_taler_exchange FROM customers - JOIN bank_accounts + JOIN bank_accounts ON customer_id=owning_customer_id WHERE login=? """) @@ -410,6 +422,7 @@ class AccountDAO(private val db: Database) { email = Option.Some(it.getString("email")), phone = Option.Some(it.getString("phone")) ), + tan_channel = it.getString("tan_channel")?.run { TanChannel.valueOf(this) }, cashout_payto_uri = it.getString("cashout_payto"), payto_uri = it.getString("internal_payto_uri"), balance = Balance( diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt index d5c89c55..58d2ca03 100644 --- a/bank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt +++ b/bank/src/main/kotlin/tech/libeufin/bank/db/CashoutDAO.kt @@ -211,7 +211,7 @@ class CashoutDAO(private val db: Database) { ,cashout_operations.subject ,creation_time ,transaction_date as confirmation_date - ,tan_channel + ,cashout_operations.tan_channel ,tan_info FROM cashout_operations JOIN bank_accounts ON bank_account=bank_account_id diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt index 7f101c82..3fb4a1d1 100644 --- a/bank/src/test/kotlin/CoreBankApiTest.kt +++ b/bank/src/test/kotlin/CoreBankApiTest.kt @@ -400,7 +400,7 @@ class CoreBankAccountsApiTest { fun reconfig() = bankSetup { _ -> authRoutine(HttpMethod.Patch, "/accounts/merchant", withAdmin = true) - // Successful attempt now. + // Successful attempt now val cashout = IbanPayTo(genIbanPaytoUri()) val req = obj { "cashout_payto_uri" to cashout.canonical @@ -410,10 +410,24 @@ class CoreBankAccountsApiTest { client.patchA("/accounts/merchant") { json(req) }.assertNoContent() - // Checking idempotence. + // Checking idempotence client.patchA("/accounts/merchant") { json(req) }.assertNoContent() + + // Check tan info + client.patchA("/accounts/merchant") { + json { + "tan_channel" to "sms" + "email" to "mail@test.com" + } + }.assertErr(TalerErrorCode.BANK_MISSING_TAN_INFO) + client.patchA("/accounts/merchant") { + json { + "tan_channel" to "email" + "phone" to "+99" + } + }.assertErr(TalerErrorCode.BANK_MISSING_TAN_INFO) checkAdminOnly( obj(req) { "debit_threshold" to "KUDOS:100" }, diff --git a/database-versioning/libeufin-bank-0002.sql b/database-versioning/libeufin-bank-0002.sql index 54a48ef0..cd26bd5c 100644 --- a/database-versioning/libeufin-bank-0002.sql +++ b/database-versioning/libeufin-bank-0002.sql @@ -21,6 +21,9 @@ SET search_path TO libeufin_bank; -- TODO remove challenges table -- TODO remove challenge and status columns from cashout_operations +ALTER TABLE customers + ADD tan_channel tan_enum NULL; + CREATE TABLE tan_challenges (challenge_id INT8 GENERATED BY DEFAULT AS IDENTITY UNIQUE ,body TEXT NOT NULL |