libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 1629f1318b95ce4a14e99fc61ab858561e0b4859
parent 5cb097630d761bc37d8654bc49ab5003513e81d3
Author: Antoine A <>
Date:   Thu, 16 Jul 2026 12:30:26 +0200

bank: reconfig password without transaction

Diffstat:
Mlibeufin-bank/src/api/account.rs | 4++--
Mlibeufin-bank/src/db/account.rs | 66+++++++++++++++++++++++++++++++++---------------------------------
Mtestbench/src/integration.rs | 4++--
3 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/libeufin-bank/src/api/account.rs b/libeufin-bank/src/api/account.rs @@ -975,7 +975,7 @@ pub mod test { } let name = "Jane"; - let payto = &rand_iban_payto(); + let payto = rand_iban_payto().full(name); let req = json!({ "username": "foo", "password": "password", @@ -986,7 +986,7 @@ pub mod test { }); // Check given payto { - let full: FullBankPayto = payto.full(name).convert(); + let full: FullBankPayto = payto.clone().convert(); // Check ok assert_eq!( full, diff --git a/libeufin-bank/src/db/account.rs b/libeufin-bank/src/db/account.rs @@ -513,46 +513,46 @@ pub async fn reconfig_password( old_pw: Option<&str>, is2fa: bool, ) -> sqlx::Result<PatchAuthResult> { - serialized!(async { - // TODO use optimistic replace instead of transaction - let mut tx = db.begin().await?; - - let Some((customer_id, currenc_pwh, tan_required)): Option<(i64, String, bool)> = - sqlx::query_as( - " + let Some((customer_id, currenc_pwh, tan_required)): Option<(i64, String, bool)> = serialized!( + sqlx::query_as( + " SELECT customer_id, password_hash, NOT $1 AND cardinality(tan_channels) > 0 FROM customers WHERE username=$2 AND deleted_at IS NULL ", - ) - .bind(is2fa) - .bind(username) - .fetch_optional(&mut *tx) - .await? - else { - return Ok(PatchAuthResult::UnknownAccount); - }; + ) + .bind(is2fa) + .bind(username) + .fetch_optional(db) + )? + else { + return Ok(PatchAuthResult::UnknownAccount); + }; - let res = if let Some(old_pw) = old_pw - && !pw_crypto.checkpw(old_pw, &currenc_pwh).unwrap().matches - { - PatchAuthResult::OldPasswordMismatch - } else if tan_required { - PatchAuthResult::TanRequired - } else { - let new_pwh = pw_crypto.hashpw(new_pw); + let res = if let Some(old_pw) = old_pw + && !pw_crypto.checkpw(old_pw, &currenc_pwh).unwrap().matches + { + PatchAuthResult::OldPasswordMismatch + } else if tan_required { + PatchAuthResult::TanRequired + } else { + let new_pwh = pw_crypto.hashpw(new_pw); + if serialized!( sqlx::query( - "UPDATE customers SET password_hash=$1, token_creation_counter=0 WHERE customer_id=$2", - ) - .bind(new_pwh) - .bind(customer_id) - .execute(&mut *tx) - .await?; + "UPDATE customers SET password_hash=$1, token_creation_counter=0 WHERE customer_id=$2 AND password_hash=$3", + ) + .bind(&new_pwh) + .bind(customer_id) + .bind(&currenc_pwh) + .execute(db) + )?.rows_affected() > 0 { PatchAuthResult::Success - }; + } else { + // If the password hash has changed, it was updated concurrently + PatchAuthResult::OldPasswordMismatch + } + }; - tx.commit().await?; - Ok(res) - }) + Ok(res) } /** Result status of customer account password check */ diff --git a/testbench/src/integration.rs b/testbench/src/integration.rs @@ -311,7 +311,7 @@ async fn errors() { // Check success let valid_payment = InTx { - subject: Some(format!("Sucess {reserve_pub}")), + subject: Some(format!("Success {reserve_pub}")), id: InId::new(None, Some("success".into()), None), ..reserve_payment }; @@ -327,7 +327,7 @@ async fn errors() { &db, &cfg, &InTx { - subject: Some(format!("Sucess 2 {reserve_pub}")), + subject: Some(format!("Success 2 {reserve_pub}")), ..valid_payment }, )