gnunet-svn
[Top][All Lists]
Advanced

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

[libeufin] branch master updated: DB side of account PATCHing


From: gnunet
Subject: [libeufin] branch master updated: DB side of account PATCHing
Date: Wed, 04 Oct 2023 13:38:33 +0200

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

ms pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new f022b731 DB side of account PATCHing
f022b731 is described below

commit f022b73168432b7cb425a5a69f41b616ce34829f
Author: MS <ms@taler.net>
AuthorDate: Wed Oct 4 13:35:34 2023 +0200

    DB side of account PATCHing
---
 .../main/kotlin/tech/libeufin/bank/BankMessages.kt | 29 +++++++++-
 .../tech/libeufin/bank/CorebankApiHandlers.kt      | 18 ++-----
 .../src/main/kotlin/tech/libeufin/bank/Database.kt | 39 ++++++++++++++
 bank/src/test/kotlin/DatabaseTest.kt               | 62 ++++++++++++++++++++++
 database-versioning/procedures.sql                 | 51 ++++++++++++++++++
 5 files changed, 184 insertions(+), 15 deletions(-)

diff --git a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
index f5c4bc5c..34b03472 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/BankMessages.kt
@@ -742,4 +742,31 @@ data class AccountReconfiguration(
     val cashout_address: String?,
     val name: String?,
     val is_exchange: Boolean
-)
\ No newline at end of file
+)
+
+/**
+ * This type expresses the outcome of updating the account
+ * data in the database.
+ */
+enum class AccountReconfigDBResult {
+    /**
+     * This indicates that despite the customer row was
+     * found in the database, its related bank account was not.
+     * This condition is a hard failure of the bank, since
+     * every customer must have one (and only one) bank account.
+     */
+    BANK_ACCOUNT_NOT_FOUND,
+
+    /**
+     * The customer row wasn't found in the database.  This error
+     * should be rare, as the client got authenticated in the first
+     * place, before the handler could try the reconfiguration in
+     * the database.
+     */
+    CUSTOMER_NOT_FOUND,
+
+    /**
+     * Reconfiguration successful.
+     */
+    SUCCESS
+}
\ No newline at end of file
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
index 9b75f874..8b269b15 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CorebankApiHandlers.kt
@@ -27,11 +27,6 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
     // TOKEN ENDPOINTS
     delete("/accounts/{USERNAME}/token") {
         val c = call.authenticateBankRequest(db, TokenScope.readonly) ?: throw 
unauthorized()
-        /**
-         * The following command ensures that this call was
-         * authenticated with the bearer token and NOT with
-         * basic auth. FIXME: this "409 Conflict" case is not documented.
-         */
         val token = call.getAuthToken() ?: throw badRequest("Basic auth not 
supported here.")
         val resourceName = call.getResourceName("USERNAME")
         /**
@@ -327,18 +322,13 @@ fun Routing.accountsMgmtHandlers(db: Database, ctx: 
BankApplicationContext) {
         if (!accountName.canI(c, withAdmin = true)) throw forbidden()
         val req = call.receive<AccountPasswordChange>()
         val hashedPassword = CryptoUtil.hashpw(req.new_password)
-        /**
-         * FIXME: should it check if the password used to authenticate
-         * FIXME: this request _is_ the one being overridden in the database?
-         */
         if (!db.customerChangePassword(
                 accountName,
                 hashedPassword
-        ))
-            throw notFound(
-                "Account '$accountName' not found (despite it being 
authenticated by this call)",
-                talerEc = TalerErrorCode.TALER_EC_END // FIXME: need at least 
GENERIC_NOT_FOUND.
-            )
+        )) throw notFound(
+            "Account '$accountName' not found (despite it being authenticated 
by this call)",
+            talerEc = TalerErrorCode.TALER_EC_END // FIXME: need at least 
GENERIC_NOT_FOUND.
+        )
         call.respond(HttpStatusCode.NoContent)
         return@patch
     }
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt 
b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
index e9b9b9a7..dc7eb04b 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Database.kt
@@ -398,6 +398,45 @@ class Database(private val dbConfig: String, private val 
bankCurrency: String) {
     }
 
     // MIXED CUSTOMER AND BANK ACCOUNT DATA
+
+    /**
+     * Updates accounts according to the PATCH /accounts/foo endpoint.
+     * The 'login' parameter decides which customer and bank account rows
+     * will get the update.
+     *
+     * The return type expresses either success, or that the target rows
+     * could not be found.
+     */
+    fun accountReconfig(
+        login: String,
+        name: String,
+        cashoutPayto: String,
+        phoneNumber: String,
+        emailAddress: String,
+        isTalerExchange: Boolean
+    ): AccountReconfigDBResult {
+        reconnect()
+        val stmt = prepare("""
+            SELECT
+              out_nx_customer,
+              out_nx_bank_account
+              FROM account_reconfig(?, ?, ?, ?, ?, ?)
+        """)
+        stmt.setString(1, login)
+        stmt.setString(2, name)
+        stmt.setString(3, phoneNumber)
+        stmt.setString(4, emailAddress)
+        stmt.setString(5, cashoutPayto)
+        stmt.setBoolean(6, isTalerExchange)
+        val res = stmt.executeQuery()
+        res.use {
+            if (!it.next()) throw internalServerError("accountReconfig() 
returned nothing")
+            if (it.getBoolean("out_nx_customer")) return 
AccountReconfigDBResult.CUSTOMER_NOT_FOUND
+            if (it.getBoolean("out_nx_bank_account")) return 
AccountReconfigDBResult.BANK_ACCOUNT_NOT_FOUND
+            return AccountReconfigDBResult.SUCCESS
+        }
+    }
+
     /**
      * Gets the list of public accounts in the system.
      * internalCurrency is the bank's currency and loginFilter is
diff --git a/bank/src/test/kotlin/DatabaseTest.kt 
b/bank/src/test/kotlin/DatabaseTest.kt
index 5e56769f..23423eb5 100644
--- a/bank/src/test/kotlin/DatabaseTest.kt
+++ b/bank/src/test/kotlin/DatabaseTest.kt
@@ -26,6 +26,9 @@ import java.time.Instant
 import java.util.Random
 import java.util.UUID
 import kotlin.experimental.inv
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+import kotlin.test.assertTrue
 
 // Foo pays Bar with custom subject.
 fun genTx(
@@ -467,4 +470,63 @@ class DatabaseTest {
         // Expecting empty, as the filter should match nothing.
         assert(db.accountsGetPublic("KUDOS", "x").isEmpty())
     }
+
+    /**
+     * Tests the UPDATE-based SQL function that backs the
+     * PATCH /accounts/foo endpoint.
+     */
+    @Test
+    fun accountReconfigTest() {
+        val db = initDb()
+        // asserting for the customer not being found.
+        db.accountReconfig(
+            "foo",
+            "Foo",
+            "payto://cashout",
+            "+99",
+            "foo@example.com",
+            true
+        ).apply { assertEquals(this, 
AccountReconfigDBResult.CUSTOMER_NOT_FOUND) }
+        // creating the customer
+        assertNotEquals(db.customerCreate(customerFoo), null)
+
+        // asserting for the bank account not being found.
+        db.accountReconfig(
+            "foo",
+            "Foo",
+            "payto://cashout",
+            "+99",
+            "foo@example.com",
+            true
+        ).apply { assertEquals(this, 
AccountReconfigDBResult.BANK_ACCOUNT_NOT_FOUND) }
+        // Giving foo a bank account
+        assert(db.bankAccountCreate(bankAccountFoo) != null)
+        // asserting for success.
+        db.accountReconfig(
+            "foo",
+            "Bar",
+            "payto://cashout",
+            "+99",
+            "foo@example.com",
+            true
+        ).apply { assertEquals(this, AccountReconfigDBResult.SUCCESS) }
+        // Getting the updated account from the database and checking values.
+        db.customerGetFromLogin("foo").apply {
+            assertNotEquals(this, null)
+            assert((this!!.login == "foo") &&
+                    (this.name == "Bar") &&
+                    (this.cashoutPayto) == "payto://cashout" &&
+                    (this.email) == "foo@example.com" &&
+                    this.phone == "+99"
+            )
+            db.bankAccountGetFromOwnerId(this.expectRowId()).apply {
+                assertNotEquals(this, null)
+                assertTrue(this!!.isTalerExchange)
+            }
+        }
+    }
 }
+
+
+
+
diff --git a/database-versioning/procedures.sql 
b/database-versioning/procedures.sql
index ad36cc2a..802ca7f4 100644
--- a/database-versioning/procedures.sql
+++ b/database-versioning/procedures.sql
@@ -86,6 +86,57 @@ END $$;
 COMMENT ON PROCEDURE bank_set_config(TEXT, TEXT)
   IS 'Update or insert configuration values';
 
+CREATE OR REPLACE FUNCTION account_reconfig(
+  IN in_login TEXT,
+  IN in_name TEXT,
+  IN in_phone TEXT,
+  IN in_email TEXT,
+  IN in_cashout_payto TEXT,
+  IN in_is_taler_exchange BOOLEAN,
+  OUT out_nx_customer BOOLEAN,
+  OUT out_nx_bank_account BOOLEAN
+)
+LANGUAGE plpgsql
+AS $$
+DECLARE
+my_customer_id INT8;
+BEGIN
+SELECT
+  customer_id
+  INTO my_customer_id
+  FROM customers
+  WHERE login=in_login;
+IF NOT FOUND
+THEN
+  out_nx_customer=TRUE;
+  RETURN;
+END IF;
+out_nx_customer=FALSE;
+
+UPDATE bank_accounts
+  SET is_taler_exchange = in_is_taler_exchange
+  WHERE owning_customer_id = my_customer_id;
+IF NOT FOUND
+THEN
+  out_nx_bank_account=TRUE;
+  RETURN;
+END IF;
+out_nx_bank_account=FALSE;
+-- bank account patching worked, custom must as well
+-- since this runs in a DB transaction and the customer
+-- was found earlier in this function.
+UPDATE customers
+SET
+  name=in_name,
+  cashout_payto=in_cashout_payto,
+  phone=in_phone,
+  email=in_email
+WHERE customer_id = my_customer_id;
+END $$;
+
+COMMENT ON FUNCTION account_reconfig(TEXT, TEXT, TEXT, TEXT, TEXT, BOOLEAN)
+  IS 'Updates values on customer and bank account rows based on the input 
data.';
+
 CREATE OR REPLACE FUNCTION customer_delete(
   IN in_login TEXT,
   OUT out_nx_customer BOOLEAN,

-- 
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]