gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-bank] branch master updated (f4d73b4 -> 1187972)


From: gnunet
Subject: [GNUnet-SVN] [taler-bank] branch master updated (f4d73b4 -> 1187972)
Date: Wed, 08 Nov 2017 19:44:47 +0100

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

marcello pushed a change to branch master
in repository bank.

    from f4d73b4  validate /add/incoming body via validictory.
     new 55d9006  checking withdraw session via json validation and moving all 
the exception handlers for wire_transfer() into one decorator.
     new 0cf476e  grouping similar calls into loop
     new 67c89e1  instantiate objects directly into the args list
     new 1187972  make /history test more compact

The 4 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:
 talerbank/app/schemas.py |  42 +++++++++----
 talerbank/app/tests.py   | 153 +++++++++++++++++++++--------------------------
 talerbank/app/views.py   | 114 +++++++++++++++++++----------------
 3 files changed, 162 insertions(+), 147 deletions(-)

diff --git a/talerbank/app/schemas.py b/talerbank/app/schemas.py
index 9effaa9..73ce80c 100644
--- a/talerbank/app/schemas.py
+++ b/talerbank/app/schemas.py
@@ -23,6 +23,34 @@ from django.conf import settings
 import validictory
 import json
 
+AMOUNT_SCHEMA = {
+    "type": "object",
+    "properties": {
+        "value": {"type": "integer"},
+        "fraction": {"type": "integer"},
+        "currency": {"type": "string",
+                     "pattern": "^"+settings.TALER_CURRENCY+"$"}
+    }
+}
+
+WITHDRAW_SESSION_SCHEMA = {
+    "type": "object",
+    "properties": {
+        "amount": {"type": AMOUNT_SCHEMA},
+        "exchange_url": {"type": "string"},
+        "reserve_pub": {"type": "string"},
+        "exchange_account_number": {"type": "integer"},
+        "sender_wiredetails": {
+            "type": "object",
+            "properties": {
+                "type": {"type": "string"},
+                "bank_uri": {"type": "string"},
+                "account_number": {"type": "integer"}
+            }
+        }
+    }
+}
+
 WIREDETAILS_SCHEMA = {
     "type": "object",
     "properties": {
@@ -46,16 +74,6 @@ AUTH_SCHEMA = {
     }
 }
 
-AMOUNT_SCHEMA = {
-    "type": "object",
-    "properties": {
-        "value": {"type": "integer"},
-        "fraction": {"type": "integer"},
-        "currency": {"type": "string",
-                     "pattern": "^"+settings.TALER_CURRENCY+"$"}
-    }
-}
-
 INCOMING_REQUEST_SCHEMA = {
     "type": "object",
     "properties": {
@@ -104,3 +122,7 @@ def validate_wiredetails(wiredetails):
 
 def validate_incoming_request(incoming_request):
     validictory.validate(incoming_request, INCOMING_REQUEST_SCHEMA)
+
+def check_withdraw_session(session):
+    validictory.validate(session, WITHDRAW_SESSION_SCHEMA)
+
diff --git a/talerbank/app/tests.py b/talerbank/app/tests.py
index 2e5a83b..8fbc4a7 100644
--- a/talerbank/app/tests.py
+++ b/talerbank/app/tests.py
@@ -29,14 +29,17 @@ def clear_db():
     BankAccount.objects.all().delete()
     BankTransaction.objects.all().delete()
 
+class WithdrawTestCase(TestCase):
+    # FIXME tbd
+    pass
 
 class RegisterTestCase(TestCase):
     """User registration"""
 
     def setUp(self):
-        bank = User.objects.create_user(username='Bank')
-        bank_bankaccount = BankAccount(user=bank)
-        bank_bankaccount.account_no = 1
+        bank_bankaccount = BankAccount(
+            user=User.objects.create_user(username='Bank'),
+            account_no=1)
         bank_bankaccount.save()
 
     def tearDown(self):
@@ -58,9 +61,10 @@ class RegisterWrongCurrencyTestCase(TestCase):
 
     # Activating this user with a faulty currency.
     def setUp(self):
-        user_bankaccount = 
BankAccount(user=User.objects.create_user(username='Bank'),
-                                       amount=Amount('WRONGCURRENCY'))
-        user_bankaccount.account_no = 1
+        user_bankaccount = BankAccount(
+            user=User.objects.create_user(username='Bank'),
+            amount=Amount('WRONGCURRENCY'),
+            account_no=1)
         user_bankaccount.save()
 
     def tearDown(self):
@@ -82,9 +86,9 @@ class LoginTestCase(TestCase):
     """User login"""
 
     def setUp(self):
-        user = User.objects.create_user(username="test_user",
-                                        password="test_password")
-        user_bankaccount = BankAccount(user=user)
+        user_bankaccount = BankAccount(
+            user=User.objects.create_user(username="test_user",
+                                          password="test_password"))
         user_bankaccount.save()
 
     def tearDown(self):
@@ -139,10 +143,10 @@ class AddIncomingTestCase(TestCase):
         bank_account = BankAccount(user=User.objects.create_user(
             username="bank_user",
             password="bank_password"))
+        bank_account.save()
         user_account = BankAccount(user=User.objects.create_user(
             username="user_user",
             password="user_password"))
-        bank_account.save()
         user_account.save()
 
     def tearDown(self):
@@ -180,7 +184,6 @@ class AddIncomingTestCase(TestCase):
                                follow=True, **{"HTTP_X_TALER_BANK_USERNAME": 
"user_user",
                                                "HTTP_X_TALER_BANK_PASSWORD": 
"user_password"})
         self.assertEqual(406, response.status_code)
-
         # Try to go debit
         data = '{"auth": {"type": "basic"}, \
                  "credit_account": 1, \
@@ -198,89 +201,69 @@ class AddIncomingTestCase(TestCase):
                                                "HTTP_X_TALER_BANK_PASSWORD": 
"user_password"})
         self.assertEqual(403, response.status_code)
 
+class HistoryContext:
+    def __init__(self, expected_resp, **kwargs):
+        self.expected_resp = expected_resp
+        self.urlargs = kwargs
+        self.urlargs.update({"auth": "basic"})
+    def dump(self):
+        return self.urlargs
 
 class HistoryTestCase(TestCase):
 
     def setUp(self):
-        user = User.objects.create_user(username='User',
-                                        password="Password")
-        user_bankaccount = BankAccount(user=user,
-                                       amount=Amount(settings.TALER_CURRENCY, 
100))
-        user_bankaccount.account_no = 1
-        user_bankaccount.save()
-        user_passive = User.objects.create_user(username='UserP',
-                                                password="PasswordP")
-        user_bankaccount_p = BankAccount(user=user_passive)
-        user_bankaccount_p.account_no = 2
-        user_bankaccount_p.save()
-        one = Amount(settings.TALER_CURRENCY, 1)
-        wire_transfer(one, user_bankaccount, user_bankaccount_p, subject="a")
-        wire_transfer(one, user_bankaccount, user_bankaccount_p, subject="b")
-        wire_transfer(one, user_bankaccount, user_bankaccount_p, subject="c")
-        wire_transfer(one, user_bankaccount, user_bankaccount_p, subject="d")
-        wire_transfer(one, user_bankaccount, user_bankaccount_p, subject="e")
-        wire_transfer(one, user_bankaccount, user_bankaccount_p, subject="f")
-        wire_transfer(one, user_bankaccount, user_bankaccount_p, subject="g")
-        wire_transfer(one, user_bankaccount, user_bankaccount_p, subject="h")
+        debit_account = BankAccount(
+            user=User.objects.create_user(username='User',
+                                          password="Password"),
+            amount=Amount(settings.TALER_CURRENCY, 100),
+            account_no=1)
+        debit_account.save()
+        credit_account = BankAccount(
+            user=User.objects.create_user(username='User0',
+                                          password="Password0"),
+            account_no=2)
+        credit_account.save()
+        for subject in ("a", "b", "c", "d", "e", "f", "g", "h"):
+            wire_transfer(Amount(settings.TALER_CURRENCY, 1),
+                          debit_account,
+                          credit_account, subject)
 
     def tearDown(self):
         clear_db()
 
     def test_history(self):
         client = Client()
-
-        response = client.get(reverse("history", urlconf=urls), {"auth": 
"basic", "delta": "+4"},
-                              **{"HTTP_X_TALER_BANK_USERNAME": "User",
-                                 "HTTP_X_TALER_BANK_PASSWORD": "Password"})
-        self.assertEqual(200, response.status_code)
-
-        # Get a delta=+1 record in the middle of the list
-        response = client.get(reverse("history", urlconf=urls),
-                              {"auth": "basic", "delta": "+1", "start": "5"},
-                              **{"HTTP_X_TALER_BANK_USERNAME": "User",
-                                 "HTTP_X_TALER_BANK_PASSWORD": "Password"})
-        data = response.content.decode("utf-8")
-        data = json.loads(data)
-        self.assertEqual(data["data"][0]["row_id"], 6)
-        # Get latest record
-        response = client.get(reverse("history", urlconf=urls),
-                              {"auth": "basic", "delta": "-1"},
-                              **{"HTTP_X_TALER_BANK_USERNAME": "User",
-                                 "HTTP_X_TALER_BANK_PASSWORD": "Password"})
-        data = response.content.decode("utf-8")
-        data = json.loads(data)
-        self.assertEqual(data["data"][0]["wt_subject"], "h")
-        # Get non-existent record: the latest plus one in the future:
-        # transaction "h" takes row_id 11
-        response = client.get(reverse("history", urlconf=urls),
-                              {"auth": "basic", "delta": "1", "start": "11"},
-                              **{"HTTP_X_TALER_BANK_USERNAME": "User",
-                                 "HTTP_X_TALER_BANK_PASSWORD": "Password"})
-        self.assertEqual(204, response.status_code)
-        # Get credit records
-        response = client.get(reverse("history", urlconf=urls),
-                              {"auth": "basic", "delta": "+1", "direction": 
"credit"},
-                              **{"HTTP_X_TALER_BANK_USERNAME": "User",
-                                 "HTTP_X_TALER_BANK_PASSWORD": "Password"})
-        self.assertEqual(204, response.status_code)
-        # Get debit records
-        response = client.get(reverse("history", urlconf=urls),
-                              {"auth": "basic", "delta": "+1", "direction": 
"debit"},
-                              **{"HTTP_X_TALER_BANK_USERNAME": "User",
-                                 "HTTP_X_TALER_BANK_PASSWORD": "Password"})
-        self.assertNotEqual(204, response.status_code)
-        # Query about non-owned account
-        response = client.get(reverse("history", urlconf=urls),
-                              {"auth": "basic", "delta": "+1", 
"account_number": 2},
-                              **{"HTTP_X_TALER_BANK_USERNAME": "User",
-                                 "HTTP_X_TALER_BANK_PASSWORD": "Password"})
-        self.assertEqual(403, response.status_code)
-        # Query about non-existent account
-        response = client.get(reverse("history", urlconf=urls),
-                              {"auth": "basic", "delta": "-1", 
"account_number": 9},
-                              **{"HTTP_X_TALER_BANK_USERNAME": "User",
-                                 "HTTP_X_TALER_BANK_PASSWORD": "Password"})
-        self.assertEqual(404, response.status_code)
+        for ctx in (HistoryContext(expected_resp={"status": 200},
+                                   delta="4"),
+                    HistoryContext(expected_resp={
+                                       "field": "row_id", "value": 6,
+                                       "status": 200}, delta="+1", start="5",),
+                    HistoryContext(expected_resp={
+                                       "field": "wt_subject", "value": "h",
+                                       "status": 200}, delta="-1"),
+                    HistoryContext(expected_resp={"status": 204},
+                                   delta="1", start="11"),
+                    HistoryContext(expected_resp={"status": 204},
+                                   delta="+1", direction="credit"),
+                    HistoryContext(expected_resp={"status": 200},
+                                   delta="+1", direction="debit"),
+                    HistoryContext(expected_resp={"status": 403},
+                                   delta="+1", account_number=2),
+                    HistoryContext(expected_resp={"status": 404},
+                                   delta="-1", account_number=9)):
+            response = client.get(reverse("history", urlconf=urls), 
ctx.urlargs,
+                                  **{"HTTP_X_TALER_BANK_USERNAME": "User",
+                                     "HTTP_X_TALER_BANK_PASSWORD": "Password"})
+            data = response.content.decode("utf-8")
+            try:
+                data = json.loads(data)["data"][0]
+            except (json.JSONDecodeError, KeyError):
+                data = {}
+
+            # FIXME print urls which break the test.
+            self.assertEqual(data.get(ctx.expected_resp.get("field")),
+                             ctx.expected_resp.get("value"))
+            self.assertEqual(ctx.expected_resp.get("status"), 
response.status_code)
 
 class DBAmountSubtraction(TestCase):
     def setUp(self):
@@ -310,8 +293,8 @@ class DebitTestCase(TestCase):
 
     def setUp(self):
         user_bankaccount = 
BankAccount(user=User.objects.create_user(username='U'))
-        user_bankaccount0 = 
BankAccount(user=User.objects.create_user(username='U0'))
         user_bankaccount.save()
+        user_bankaccount0 = 
BankAccount(user=User.objects.create_user(username='U0'))
         user_bankaccount0.save()
 
     def test_green(self):
diff --git a/talerbank/app/views.py b/talerbank/app/views.py
index 2093d2f..58580af 100644
--- a/talerbank/app/views.py
+++ b/talerbank/app/views.py
@@ -15,6 +15,16 @@
 #  @author Marcello Stanisci
 #  @author Florian Dold
 
+# wire_transfer() needs to be wrapped in such a way that
+# any possible exception is caught in *one* place.  It is used when:
+#
+# 1. withdrawing is finalized (pin_tan_verify())
+# 2. a new user is registered (register())
+# 3. the exchange moves money to account X (add_incoming())
+#
+# NOTE: this abstracting function needs _sometimes_ to update the
+# session, depending on the situation.
+
 from urllib.parse import urljoin
 from functools import wraps
 import json
@@ -64,7 +74,6 @@ def ignore(request):
     del request
     return HttpResponse()
 
-
 def javascript_licensing(request):
     return render(request, "javascript.html")
 
@@ -139,7 +148,7 @@ def pin_tan_question(request):
         schemas.validate_pin_tan_args(request.GET.dict())
         # Currency is not checked, as any mismatches will be
         # detected afterwards
-    except validictory.FieldValidationError as err:
+    except (FVE, RFVE) as err:
         return HRBR("invalid '%s'" % err.fieldname)
     user_account = BankAccount.objects.get(user=request.user)
     request.session["exchange_account_number"] = \
@@ -163,9 +172,6 @@ def pin_tan_question(request):
         exchange=request.GET["exchange"])
     return render(request, "pin_tan.html", context)
 
-def err_ctx(resp, msg):
-    return dict(resp=resp, msg=msg)
-
 @require_POST
 @login_required
 def pin_tan_verify(request):
@@ -178,31 +184,26 @@ def pin_tan_verify(request):
         request.session["captcha_failed"] = True
         return redirect(request.POST.get("question_url", "profile"))
     # Check the session is a "pin tan" one
-    for i in ("amount", "exchange_url", "reserve_pub",
-              "exchange_account_number", "sender_wiredetails"):
-        if i not in request.session:
-            LOGGER.warning("Apparently NOT a withdraw session")
-            return redirect("profile")
-    amount = Amount(**request.session["amount"])
     try:
+        schemas.check_withdraw_session(request.session)
+        amount = Amount(**request.session["amount"])
         exchange_bank_account = BankAccount.objects.get(
             account_no=request.session["exchange_account_number"])
         wire_transfer(amount,
                       BankAccount.objects.get(user=request.user),
                       exchange_bank_account,
-                      request.session["reserve_pub"])
+                      request.session["reserve_pub"],
+                      request,
+                      session_expand=dict(debt_limit=True))
+    except (FVE, RFVE) as exc:
+        LOGGER.warning("Not a withdrawing session")
+        return redirect("profile")
+
     except BankAccount.DoesNotExist as exc:
-        err = err_ctx(HRBR("That exchange is unknown to this bank"),
-                      exc)
-    except DebtLimitExceededException as exc:
-        request.session["debt_limit"] = True
-        err = err_ctx(redirect("profile"), exc)
-    except (SameAccountException, BadFormatAmount, CurrencyMismatch) as exc:
-        err = err_ctx(JsonResponse({"error": "Internal server error"}, 
status=500),
-                      exc)
-    if "err" in locals():
-        LOGGER.error(err["msg"])
-        return err["resp"]()
+        return JsonResponse({"error": "That exchange is unknown to this bank"},
+                            status=404)
+    except WireTransferException as exc:
+        return exc.response
     res = requests.post(
         urljoin(request.session["exchange_url"],
                 "admin/add/incoming"),
@@ -250,18 +251,13 @@ def register(request):
     bank_internal_account = BankAccount.objects.get(account_no=1)
     try:
         wire_transfer(Amount(settings.TALER_CURRENCY, 100, 0),
-                      bank_internal_account, user_account,
-                      "Joining bonus")
-    except (CurrencyMismatch,
-            BadFormatAmount,
-            SameAccountException) as exc:
-        err = err_ctx(HttpResponseServerError(), exc)
-    except DebtLimitExceededException as exc:
-        request.session["no_initial_bonus"] = True
-        err = err_ctx(HttpResponseServerError(), exc)
-    if "err" in locals():
-        LOGGER.error(err["msg"])
-        return err["resp"]
+                      bank_internal_account,
+                      user_account,
+                      "Joining bonus",
+                      request=request,
+                      session_expand=dict(no_initial_bobus=True))
+    except WireTransferException as exc:
+        return exc.response
     request.session["just_registered"] = True
     user = django.contrib.auth.authenticate(username=username, 
password=password)
     django.contrib.auth.login(request, user)
@@ -447,9 +443,9 @@ def add_incoming(request, user_account):
     except (FVE, RFVE) as exc:
         return JsonResponse({"error": "invalid '%s'" % exc.fieldname},
                             status=406 if exc.fieldname == "currency" else 400)
+
     try:
         credit_account = BankAccount.objects.get(user=data["credit_account"])
-        schemas.validate_amount(data["amount"])
         wtrans = wire_transfer(Amount(**data["amount"]),
                                user_account.bankaccount,
                                credit_account,
@@ -457,22 +453,8 @@ def add_incoming(request, user_account):
     except BankAccount.DoesNotExist:
         return JsonResponse({"error": "credit_account not found"},
                             status=404)
-    except ValueError as exc:
-        err = err_ctx(JsonResponse({"error": exc}, status=400), exc)
-    except (CurrencyMismatch, BadFormatAmount) as exc:
-        err = err_ctx(JsonResponse({"error": "Internal server error"},
-                                   status=500),
-                      exc)
-    except SameAccountException:
-        err = err_ctx(JsonResponse({"error":"same debit and credit account"},
-                                   status=422),
-                      exc)
-    except DebtLimitExceededException as exc:
-        err = err_ctx(JsonResponse({"error": "debt situation"}, status=403),
-                      exc)
-    if "err" in locals():
-        LOGGER.error(err["msg"])
-        return err["resp"]
+    except WireTransferException as exc:
+        return exc.response
     return JsonResponse({"serial_id": wtrans.id,
                          "timestamp":
                              "/Date(%s)/" % int(wtrans.date.timestamp())})
@@ -503,7 +485,35 @@ def withdraw_nojs(request):
         response["X-Taler-Suggested-Exchange"] = 
settings.TALER_SUGGESTED_EXCHANGE
     return response
 
+class WireTransferException(Exception):
+    def __init__(self, response):
+        self.response = response
+
+def wire_transfer_exc_handler(view_func):
+    def err_cb(exc, resp):
+        LOGGER.error(str(exc))
+        raise WireTransferException(resp)
+    def _decorator(*args, request=None, session_expand=None):
+        try:
+            return view_func(*args)
+        except (CurrencyMismatch, BadFormatAmount) as exc:
+            err_cb(exc, JsonResponse({"error": "internal server error"},
+                                     status=500))
+        except DebtLimitExceededException as exc:
+            if request:
+                if session_expand:
+                    request.session.update(session_expand)
+                if request.path == "/pin/verify":
+                    err_cb(exc, redirect("profile"))
+            else:
+                err_cb(exc, JsonResponse({"error": "Unallowed debit"},
+                                         status=403))
+        except SameAccountException as exc:
+            err_cb(exc, JsonResponse({"error": "sender account == receiver 
account"},
+                                     status=422))
+    return wraps(view_func)(_decorator)
 
address@hidden
 def wire_transfer(amount, debit_account, credit_account, subject):
     LOGGER.info("%s => %s, %s, %s" %
                 (debit_account.account_no,

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

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