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: introducing headless wi


From: gnunet
Subject: [GNUnet-SVN] [taler-bank] branch master updated: introducing headless withdrawal
Date: Sun, 30 Jun 2019 20:55:36 +0200

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

marcello pushed a commit to branch master
in repository bank.

The following commit(s) were added to refs/heads/master by this push:
     new 24e28e0  introducing headless withdrawal
24e28e0 is described below

commit 24e28e0bfab5d75166bb11e9682fb6079cceb9d7
Author: Marcello Stanisci <address@hidden>
AuthorDate: Sun Jun 30 20:55:26 2019 +0200

    introducing headless withdrawal
---
 bank-check.conf             |  2 +-
 talerbank/app/middleware.py |  4 +-
 talerbank/app/models.py     |  2 +-
 talerbank/app/schemas.py    | 11 +++++-
 talerbank/app/tests.py      | 93 ++++++++++++++++++++++++++++++++++++++++++++-
 talerbank/app/urls.py       |  2 +-
 talerbank/app/views.py      | 45 +++++++++++++++++-----
 talerbank/settings.py       |  2 +
 8 files changed, 144 insertions(+), 17 deletions(-)

diff --git a/bank-check.conf b/bank-check.conf
index 65be040..b977fd9 100644
--- a/bank-check.conf
+++ b/bank-check.conf
@@ -18,4 +18,4 @@ MAX_DEBT_BANK = KUDOS:0.0
 UWSGI_SERVE = unix
 UWSGI_UNIXPATH = /tmp/banktest-unused.uwsgi
 UWSGI_UNIXPATH_MODE = 660
-
+SUGGESTED_EXCHANGE_PAYTO = payto://x-taler-bank/bank.example.com/2
diff --git a/talerbank/app/middleware.py b/talerbank/app/middleware.py
index d7c3775..e2ba00b 100644
--- a/talerbank/app/middleware.py
+++ b/talerbank/app/middleware.py
@@ -73,7 +73,6 @@ class ExceptionMiddleware:
             BankTransaction.DoesNotExist: 1,
             SameAccountException: 2,
             DebitLimitException: 3,
-
             ##
             # FIXME: needs own error code.
             InvalidSession: 0,
@@ -96,6 +95,7 @@ class ExceptionMiddleware:
         # to generate exceptions.
         self.apis = {
             "/withdraw": 5400,
+            "/taler/withdraw": 5500,
             "/reject": 5300,
             "/history": 5200,
             "/admin/add/incoming": 5100}
@@ -159,7 +159,7 @@ class ExceptionMiddleware:
         # This exception happens when the middleware is catching
         # DoesNotExist exceptions; the ideal fix is to get BankAccount
         # and BankTransaction classes to override their 'DoesNotExist'
-        # field wiht some custom class, but that wasn't straightforward
+        # field with some custom class, but that wasn't straightforward
         # (in the sense that on different systems we had different
         # results, so we fallback on this more sound / manual approach)
         except AttributeError:
diff --git a/talerbank/app/models.py b/talerbank/app/models.py
index e617aba..b930030 100644
--- a/talerbank/app/models.py
+++ b/talerbank/app/models.py
@@ -26,7 +26,7 @@ from django.conf import settings
 from django.core.exceptions import \
     ValidationError, \
     ObjectDoesNotExist
-from .amount import Amount, BadFormatAmount, NumberTooBig
+from .amount import Amount, BadFormatAmount, NumberTooBig, CurrencyMismatch
 
 class InvalidAmount(Amount):
     def __init__(self, currency):
diff --git a/talerbank/app/schemas.py b/talerbank/app/schemas.py
index db2ce7b..ee79abe 100644
--- a/talerbank/app/schemas.py
+++ b/talerbank/app/schemas.py
@@ -177,11 +177,20 @@ class HistoryRangeParams(HistoryParamsBase):
     start = forms.IntegerField()
 
 class PaytoField(forms.Field):
-    # TODO: try removing this method.
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+
     def to_python(self, value):
         return value
 
     def validate(self, value):
+
+        # The request misses this, default exchange
+        # will be used.  NOTE: experience showed that the
+        # "required=False" argument given when init the object
+        # does NOT prevent this function from being called!
+        if not value:
+            return
         wire_uri = urlparse(value)
         if "payto" != wire_uri.scheme:
             raise ValidationError("URL is not 'payto'")
diff --git a/talerbank/app/tests.py b/talerbank/app/tests.py
index 9a14498..80ddeb2 100644
--- a/talerbank/app/tests.py
+++ b/talerbank/app/tests.py
@@ -184,6 +184,7 @@ class RegisterTestCase(TestCase):
     def test_register_headless(self):
         client = Client()
 
+        # Normal case.
         response = client.post(reverse("register-headless", urlconf=urls),
                                {"username": "test_register_headless",
                                 "password": "password*+#@"})
@@ -193,12 +194,13 @@ class RegisterTestCase(TestCase):
         self.assertTrue(self.client.login(username="test_register_headless",
                                           password="password*+#@"))
 
+        # Try registering unavailable username.
         response = client.post(reverse("register-headless", urlconf=urls),
                                {"username": "test_register_headless",
                                 "password": "password"})
         self.assertEqual(409, response.status_code)
 
-        # NOTE: Django 2.2.2 allows ANY character!
+        # NOTE: Django 2.2.2 allows ANY character!  Is this normal?
         response = client.post(reverse("register-headless", urlconf=urls),
                                {"username": "'''+++;;;'''",
                                 "password": "password2"})
@@ -300,6 +302,93 @@ class RejectTestCase(TestCase):
         self.assertEqual(response.status_code, 204)
 
 
+class WithdrawHeadlessTestCase(TestCase):
+
+    def setUp(self):
+        BankAccount(
+            user=User.objects.create_user(
+                username="headless_wallet",
+                password="headless_password"),
+            amount=Amount(settings.TALER_CURRENCY, 10)).save()
+        # Gets account #2, in line with config.
+        BankAccount(user=User.objects.create_user(
+            username="normal_exchange",
+            password="normal_password")).save()
+
+    def test_withdraw_headless(self):
+        client = Client()
+
+        # Use default exchange.
+        data = '{"auth": {"type": "basic"}, \
+                 "reserve_pub": "RESERVEPUB", \
+                 "amount": "%s:10"}' % settings.TALER_CURRENCY
+        response = client.post(
+            reverse("withdraw-headless", urlconf=urls),
+            data=data,
+            content_type="application/json",
+            follow=True,
+            **{"HTTP_X_TALER_BANK_USERNAME": "headless_wallet",
+               "HTTP_X_TALER_BANK_PASSWORD": "headless_password"})
+        self.assertEqual(200, response.status_code)
+
+        # Try withdrawing more than owning.
+        data = '{"auth": {"type": "basic"}, \
+                 "reserve_pub": "RESERVEPUB", \
+                 "amount": "%s:100"}' % settings.TALER_CURRENCY
+        response = client.post(
+            reverse("withdraw-headless", urlconf=urls),
+            data=data,
+            content_type="application/json",
+            follow=True,
+            **{"HTTP_X_TALER_BANK_USERNAME": "headless_wallet",
+               "HTTP_X_TALER_BANK_PASSWORD": "headless_password"})
+        self.assertEqual(406, response.status_code)
+
+        # Try withdrawing giving exchange field.
+        data = '{"auth": {"type": "basic"}, \
+                 "exchange_wire_details": 
"payto://x-taler-bank/bank.example.com/2", \
+                 "reserve_pub": "RESERVEPUB", \
+                 "amount": "%s:0.4"}' % settings.TALER_CURRENCY
+        response = client.post(
+            reverse("withdraw-headless", urlconf=urls),
+            data=data,
+            content_type="application/json",
+            follow=True,
+            **{"HTTP_X_TALER_BANK_USERNAME": "headless_wallet",
+               "HTTP_X_TALER_BANK_PASSWORD": "headless_password"})
+        self.assertEqual(200, response.status_code)
+
+        # Try withdrawing giving non-existent recipient.
+        data = '{"auth": {"type": "basic"}, \
+                 "exchange_wire_details": 
"payto://x-taler-bank/bank.example.com/2222", \
+                 "reserve_pub": "RESERVEPUB", \
+                 "amount": "%s:0.4"}' % settings.TALER_CURRENCY
+        response = client.post(
+            reverse("withdraw-headless", urlconf=urls),
+            data=data,
+            content_type="application/json",
+            follow=True,
+            **{"HTTP_X_TALER_BANK_USERNAME": "headless_wallet",
+               "HTTP_X_TALER_BANK_PASSWORD": "headless_password"})
+        self.assertEqual(404, response.status_code)
+
+        # Try withdrawing giving invalid JSON.
+        data = '{"auth": {"type": "basic"}, \
+                 "XXX": "YYY", \
+                 "amount": "%s:0.4"}' % settings.TALER_CURRENCY
+        response = client.post(
+            reverse("withdraw-headless", urlconf=urls),
+            data=data,
+            content_type="application/json",
+            follow=True,
+            **{"HTTP_X_TALER_BANK_USERNAME": "headless_wallet",
+               "HTTP_X_TALER_BANK_PASSWORD": "headless_password"})
+        self.assertEqual(400, response.status_code)
+
+    def tearDown(self):
+        clear_db()
+
+
 class AddIncomingTestCase(TestCase):
     """Test money transfer's API"""
 
@@ -372,7 +461,7 @@ class AddIncomingTestCase(TestCase):
             follow=True,
             **{"HTTP_X_TALER_BANK_USERNAME": "user_user",
                "HTTP_X_TALER_BANK_PASSWORD": "user_password"})
-        self.assertEqual(403, response.status_code)
+        self.assertEqual(406, response.status_code)
         # Try use a non-existent recipient.
         data = '{"auth": {"type": "basic"}, \
                  "credit_account": 1987, \
diff --git a/talerbank/app/urls.py b/talerbank/app/urls.py
index 8a2d1e5..d9b4491 100644
--- a/talerbank/app/urls.py
+++ b/talerbank/app/urls.py
@@ -42,7 +42,7 @@ urlpatterns = [
     url(r'^history-range$', views.serve_history_range, name="history-range"),
     url(r'^reject$', views.reject, name="reject"),
     url(r'^withdraw$', views.withdraw_nojs, name="withdraw-nojs"),
-    url(r'^taler/withdraw$', views.withdraw_nojs, name="withdraw-headless"),
+    url(r'^taler/withdraw$', views.withdraw_headless, 
name="withdraw-headless"),
     url(r'^public-accounts$', views.serve_public_accounts,
         name="public-accounts"),
     url(r'^public-accounts/(?P<name>[a-zA-Z0-9]+)$',
diff --git a/talerbank/app/views.py b/talerbank/app/views.py
index e868a4b..dbf8940 100644
--- a/talerbank/app/views.py
+++ b/talerbank/app/views.py
@@ -49,7 +49,7 @@ from .schemas import \
      URLParamValidationError, RejectData,
      AddIncomingData, JSONFieldException,
      PinTanParams, InvalidSession,
-     WithdrawSessionData)
+     WithdrawSessionData, WithdrawHeadless)
 
 LOGGER = logging.getLogger(__name__)
 
@@ -81,13 +81,12 @@ class PrivateAccountException(Exception):
     hint = "The selected account is private"
     http_status_code = 402
 
-
 ##
 # Exception raised when some financial operation goes
 # beyond the limit threshold.
 class DebitLimitException(Exception):
-    hint = "Debit too high, operation forbidden."
-    http_status_code = 403
+    hint = "Insufficient credit, operation not acceptable."
+    http_status_code = 406
 
 ##
 # Exception raised when some financial operation is
@@ -320,11 +319,10 @@ def make_question():
     question = "{} {} {}".format(num1, operand, num2)
     return question, hash_answer(answer)
 
-
 def get_acct_from_payto(uri_str: str) -> int:
     wire_uri = urlparse(uri_str)
     if wire_uri.scheme != "payto":
-        raise Exception("wire URI must be a payto URI")
+        raise Exception("Bad Payto URI: '%s'" % uri_str)
     return int(wire_uri.path.split("/")[-1])
 
 
@@ -923,10 +921,39 @@ def add_incoming(request, user_account):
 @login_via_headers
 @csrf_exempt
 @require_POST
-def withdraw_headless(request):
-    pass
+def withdraw_headless(request, user):
+    data = json.loads(request.body.decode("utf-8"))
+    data = WithdrawHeadless(data)
+
+    sender_payto = "payto://x-taler-bank/%s/%d" % \
+        (request.get_host(), user.bankaccount.account_no)
+    ret_obj = (
+        {"sender_wire_details": sender_payto})
+
+    if not data.is_valid():
+        raise JSONFieldException(data.errors, 400)
+
+    # Pick default exchange.
+    if None == data.cleaned_data["exchange_wire_details"]:
+        exchange_accno = get_acct_from_payto(
+            settings.TALER_SUGGESTED_EXCHANGE_PAYTO)
+        ret_obj.update(
+            exchange_url=settings.TALER_SUGGESTED_EXCHANGE)
+    else:
+        exchange_accno = get_acct_from_payto(
+            data.cleaned_data["exchange_wire_details"])
 
+    exchange_bankaccount = BankAccount.objects.get(
+        account_no=exchange_accno)
 
+    wire_transfer(
+        Amount.parse(data.cleaned_data["amount"]),
+        user.bankaccount,
+        exchange_bankaccount,
+        data.cleaned_data["reserve_pub"])
+
+    return JsonResponse(ret_obj)
+    
 ##
 # Serve a Taler withdrawal request; takes the amount chosen
 # by the user, and builds a response to trigger the wallet into
@@ -959,7 +986,7 @@ def withdraw_nojs(request):
 ##
 # Make a wire transfer between two accounts (internal to the bank)
 #
-# @param amount how much money the wire transfer is worth.
+# @param amount (object type) how much money the wire transfer is worth.
 #        FIXME: a check about whether this value is zero is missing
 # @param debit_account the account that gives money.
 # @param credit_account the account that receives money.
diff --git a/talerbank/settings.py b/talerbank/settings.py
index f3f099d..44fcf70 100644
--- a/talerbank/settings.py
+++ b/talerbank/settings.py
@@ -209,3 +209,5 @@ TALER_EXPECTS_DONATIONS = [
     'Tor', 'GNUnet', 'Taler', 'FSF']
 TALER_SUGGESTED_EXCHANGE = TC.value_string(
     "bank", "suggested_exchange")
+TALER_SUGGESTED_EXCHANGE_PAYTO = TC.value_string(
+    "bank", "suggested_exchange_payto")

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



reply via email to

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