gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated (61c49361 -> 1e3e0927)


From: gnunet
Subject: [libeufin] branch master updated (61c49361 -> 1e3e0927)
Date: Tue, 14 Nov 2023 00:38:07 +0100

This is an automated email from the git hooks/post-receive script.

antoine pushed a change to branch master
in repository libeufin.

    from 61c49361 rm idea files
     new cb7094fe Improve accounts API
     new 1e3e0927 Improve cashout API

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../main/kotlin/tech/libeufin/bank/CoreBankApi.kt  | 41 ++++++++++-----
 bank/src/main/kotlin/tech/libeufin/bank/Main.kt    |  2 +-
 .../main/kotlin/tech/libeufin/bank/TalerMessage.kt |  3 +-
 .../main/kotlin/tech/libeufin/bank/db/Database.kt  | 40 +++++++++++----
 bank/src/test/kotlin/CoreBankApiTest.kt            | 60 +++++++++++++++++-----
 util/src/main/kotlin/TalerErrorCode.kt             | 48 +++++++++++++++++
 6 files changed, 156 insertions(+), 38 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
index ad87078d..165850dd 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -221,7 +221,10 @@ private fun Routing.coreBankAccountsMgmtApi(db: Database, 
ctx: BankConfig) {
             req.debit_threshold?.run { ctx.checkRegionalCurrency(this) }
 
             if (req.is_taler_exchange != null && username == "admin")
-                throw forbidden("admin account cannot be an exchange")
+                throw conflict(
+                    "admin account cannot be an exchange",
+                    TalerErrorCode.BANK_PATCH_ADMIN_EXCHANGE
+                )
 
             val res = db.accountReconfig(
                 login = username,
@@ -239,27 +242,38 @@ private fun Routing.coreBankAccountsMgmtApi(db: Database, 
ctx: BankConfig) {
                     "Account '$username' not found",
                     TalerErrorCode.BANK_UNKNOWN_ACCOUNT
                 )
-                CustomerPatchResult.CONFLICT_LEGAL_NAME -> 
-                    throw forbidden("non-admin user cannot change their legal 
name")
-                CustomerPatchResult.CONFLICT_DEBT_LIMIT -> 
-                    throw forbidden("non-admin user cannot change their debt 
limit")
+                CustomerPatchResult.CONFLICT_LEGAL_NAME -> throw conflict(
+                    "non-admin user cannot change their legal name",
+                    TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME
+                )
+                CustomerPatchResult.CONFLICT_DEBT_LIMIT -> throw conflict(
+                    "non-admin user cannot change their debt limit",
+                    TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT
+                )
             }
         }
-    }
-    auth(db, TokenScope.readwrite) {
         patch("/accounts/{USERNAME}/auth") {
             val req = call.receive<AccountPasswordChange>()
-            val hashedPassword = CryptoUtil.hashpw(req.new_password)
-            if (!db.customerChangePassword(username, hashedPassword))
-                throw notFound(
+            if (!isAdmin && req.old_password == null) {
+                throw conflict(
+                    "non-admin user cannot change password without providing 
old password",
+                    TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD
+                )
+            }
+            when (db.accountReconfigPassword(username, req.new_password, 
req.old_password)) {
+                CustomerPatchAuthResult.SUCCESS -> 
call.respond(HttpStatusCode.NoContent)
+                CustomerPatchAuthResult.ACCOUNT_NOT_FOUND -> throw notFound(
                     "Account '$username' not found",
                     TalerErrorCode.BANK_UNKNOWN_ACCOUNT
                 )
-            call.respond(HttpStatusCode.NoContent)
+                CustomerPatchAuthResult.CONFLICT_BAD_PASSWORD -> throw 
conflict(
+                    "old password does not match",
+                    TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD
+                )
+            }
         }
     }
     get("/public-accounts") {
-        // no authentication here.
         val publicAccounts = db.accountsGetPublic(ctx.currency)
         if (publicAccounts.isEmpty()) {
             call.respond(HttpStatusCode.NoContent)
@@ -538,8 +552,7 @@ private fun Routing.coreBankCashoutApi(db: Database, ctx: 
BankConfig) = conditio
                             process.exitValue()
                         }
                         if (exitValue != 0) {
-                            throw libeufinError(
-                                HttpStatusCode.BadGateway,
+                            throw conflict(
                                 "Tan channel script failure with exit value 
$exitValue",
                                 TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED
                             )
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index 00094213..8bb421c7 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -331,7 +331,7 @@ class ChangePw : CliktCommand("Change account password", 
name = "passwd") {
             if (!maybeCreateAdminAccount(db, ctx)) // logs provided by the 
helper
             exitProcess(1)
 
-            if (!db.customerChangePassword(account, 
CryptoUtil.hashpw(password))) {
+            if (db.accountReconfigPassword(account, password, null) != 
CustomerPatchAuthResult.SUCCESS) {
                 println("password change failed")
                 exitProcess(1)
             } else {
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
index c8d176fe..b91efe7a 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
@@ -609,5 +609,6 @@ data class PublicAccount(
  */
 @Serializable
 data class AccountPasswordChange(
-    val new_password: String
+    val new_password: String,
+    val old_password: String? = null
 )
\ No newline at end of file
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/Database.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/db/Database.kt
index 2cf5e7e5..863347e1 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/Database.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/Database.kt
@@ -110,15 +110,6 @@ class Database(dbConfig: String, internal val 
bankCurrency: String, internal val
         }
     }
 
-    suspend fun customerChangePassword(customerName: String, passwordHash: 
String): Boolean = conn { conn ->
-        val stmt = conn.prepareStatement("""
-            UPDATE customers SET password_hash=? where login=?
-        """)
-        stmt.setString(1, passwordHash)
-        stmt.setString(2, customerName)
-        stmt.executeUpdateCheck()
-    }
-
     suspend fun customerPasswordHashFromLogin(login: String): String? = conn { 
conn ->
         val stmt = conn.prepareStatement("""
             SELECT password_hash FROM customers WHERE login=?
@@ -438,6 +429,30 @@ class Database(dbConfig: String, internal val 
bankCurrency: String, internal val
         }
     }
 
+    suspend fun accountReconfigPassword(login: String, newPw: String, oldPw: 
String?): CustomerPatchAuthResult = conn {
+        it.transaction { conn ->
+            val currentPwh = conn.prepareStatement("""
+                SELECT password_hash FROM customers WHERE login=?
+            """).run {
+                setString(1, login)
+                oneOrNull { it.getString(1) }
+            }
+            if (currentPwh == null) {
+                CustomerPatchAuthResult.ACCOUNT_NOT_FOUND
+            } else if (oldPw != null && !CryptoUtil.checkpw(oldPw, 
currentPwh)) {
+                CustomerPatchAuthResult.CONFLICT_BAD_PASSWORD
+            } else {
+                val stmt = conn.prepareStatement("""
+                    UPDATE customers SET password_hash=? where login=?
+                """)
+                stmt.setString(1, CryptoUtil.hashpw(newPw))
+                stmt.setString(2, login)
+                stmt.executeUpdateCheck()
+                CustomerPatchAuthResult.SUCCESS
+            }
+        }
+    }
+
     /**
      * Gets the list of public accounts in the system.
      * internalCurrency is the bank's currency and loginFilter is
@@ -943,6 +958,13 @@ enum class CustomerPatchResult {
     SUCCESS
 }
 
+/** Result status of customer account auth patch */
+enum class CustomerPatchAuthResult {
+    ACCOUNT_NOT_FOUND,
+    CONFLICT_BAD_PASSWORD,
+    SUCCESS
+}
+
 /** Result status of customer account deletion */
 enum class CustomerDeletionResult {
     SUCCESS,
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt 
b/bank/src/test/kotlin/CoreBankApiTest.kt
index b0d4289a..e14f361d 100644
--- a/bank/src/test/kotlin/CoreBankApiTest.kt
+++ b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -352,12 +352,12 @@ class CoreBankAccountsMgmtApiTest {
             jsonBody(req)
         }.assertNoContent()
 
-        suspend fun checkAdminOnly(req: JsonElement) {
+        suspend fun checkAdminOnly(req: JsonElement, error: TalerErrorCode) {
             // Checking ordinary user doesn't get to patch
             client.patch("/accounts/merchant") {
                 basicAuth("merchant", "merchant-password")
                 jsonBody(req)
-            }.assertForbidden()
+            }.assertConflict(error)
             // Finally checking that admin does get to patch
             client.patch("/accounts/merchant") {
                 basicAuth("admin", "admin-password")
@@ -365,14 +365,20 @@ class CoreBankAccountsMgmtApiTest {
             }.assertNoContent()
         }
 
-        checkAdminOnly(json(req) { "name" to "Another Foo" })
-        checkAdminOnly(json(req) { "debit_threshold" to "KUDOS:100" })
+        checkAdminOnly(
+            json(req) { "name" to "Another Foo" },
+            TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME
+        )
+        checkAdminOnly(
+            json(req) { "debit_threshold" to "KUDOS:100" },
+            TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT
+        )
 
         // Check admin account cannot be exchange
         client.patch("/accounts/admin") {
             basicAuth("admin", "admin-password")
             jsonBody { "is_taler_exchange" to true }
-        }.assertForbidden()
+        }.assertConflict(TalerErrorCode.BANK_PATCH_ADMIN_EXCHANGE)
         // But we can change its debt limit
         client.patch("/accounts/admin") {
             basicAuth("admin", "admin-password")
@@ -402,21 +408,49 @@ class CoreBankAccountsMgmtApiTest {
     @Test
     fun passwordChangeTest() = bankSetup { _ -> 
         // Changing the password.
-        client.patch("/accounts/merchant/auth") {
-            basicAuth("merchant", "merchant-password")
+        client.patch("/accounts/customer/auth") {
+            basicAuth("customer", "customer-password")
             jsonBody {
+                "old_password" to "customer-password"
                 "new_password" to "new-password"
             }
         }.assertNoContent()
         // Previous password should fail.
-        client.patch("/accounts/merchant/auth") {
-            basicAuth("merchant", "merchant-password")
+        client.patch("/accounts/customer/auth") {
+            basicAuth("customer", "customer-password")
         }.assertUnauthorized()
         // New password should succeed.
-        client.patch("/accounts/merchant/auth") {
-            basicAuth("merchant", "new-password")
+        client.patch("/accounts/customer/auth") {
+            basicAuth("customer", "new-password")
             jsonBody {
-                "new_password" to "merchant-password"
+                "old_password" to "new-password"
+                "new_password" to "customer-password"
+            }
+        }.assertNoContent()
+
+
+        // Check require test old password
+        client.patch("/accounts/customer/auth") {
+            basicAuth("customer", "customer-password")
+            jsonBody {
+                "old_password" to "bad-password"
+                "new_password" to "new-password"
+            }
+        }.assertConflict(TalerErrorCode.BANK_PATCH_BAD_OLD_PASSWORD)
+
+        // Check require old password for user
+        client.patch("/accounts/customer/auth") {
+            basicAuth("customer", "customer-password")
+            jsonBody {
+                "new_password" to "new-password"
+            }
+        
}.assertConflict(TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD)
+
+        // Check admin 
+        client.patch("/accounts/customer/auth") {
+            basicAuth("admin", "admin-password")
+            jsonBody {
+                "new_password" to "new-password"
             }
         }.assertNoContent()
     }
@@ -1039,7 +1073,7 @@ class CoreBankCashoutApiTest {
             jsonBody(req) {
                 "tan_channel" to "email"
             }
-        }.assertStatus(HttpStatusCode.BadGateway, 
TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED)
+        }.assertConflict(TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED)
 
         // Check OK
         client.post("/accounts/customer/cashouts") {
diff --git a/util/src/main/kotlin/TalerErrorCode.kt 
b/util/src/main/kotlin/TalerErrorCode.kt
index 126b899f..70f08e34 100644
--- a/util/src/main/kotlin/TalerErrorCode.kt
+++ b/util/src/main/kotlin/TalerErrorCode.kt
@@ -665,6 +665,14 @@ enum class TalerErrorCode(val code: Int) {
   EXCHANGE_GENERIC_AML_FROZEN(1036),
 
 
+  /**
+   * The exchange failed to start a KYC attribute conversion helper process. 
It is likely configured incorrectly.
+   * Returned with an HTTP status code of #MHD_HTTP_INTERNAL_SERVER_ERROR 
(500).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  EXCHANGE_GENERIC_KYC_CONVERTER_FAILED(1037),
+
+
   /**
    * The exchange did not find information about the specified transaction in 
the database.
    * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
@@ -3425,6 +3433,46 @@ enum class TalerErrorCode(val code: Int) {
   BANK_TAN_CHALLENGE_FAILED(5134),
 
 
+  /**
+   * A non-admin user has tried to change their legal name.
+   * 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_LEGAL_NAME(5135),
+
+
+  /**
+   * A non-admin user has tried to change their debt limit.
+   * 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_DEBT_LIMIT(5136),
+
+
+  /**
+   * A non-admin user has tried to change their password whihout providing the 
current one.
+   * 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_MISSING_OLD_PASSWORD(5137),
+
+
+  /**
+   * Provided old password does not match current password.
+   * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  BANK_PATCH_BAD_OLD_PASSWORD(5138),
+
+
+  /**
+   * An admin user has tried to become an exchange.
+   * Returned with an HTTP status code of #MHD_HTTP_CONFLICT (409).
+   * (A value of 0 indicates that the error is generated client-side).
+   */
+  BANK_PATCH_ADMIN_EXCHANGE(5139),
+
+
   /**
    * The sync service failed find the account in its database.
    * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]