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 (79734f4 -> c431800)


From: gnunet
Subject: [GNUnet-SVN] [taler-bank] branch master updated (79734f4 -> c431800)
Date: Fri, 31 May 2019 20:17:07 +0200

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

marcello pushed a change to branch master
in repository bank.

    from 79734f4  porting add-incoming
     new 47d6ec4  porting pin-question
     new c431800  5715.

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:
 requirements.txt            |   1 -
 setup.py                    |   1 -
 talerbank/app/middleware.py |  13 ++-
 talerbank/app/schemas.py    | 279 ++++++++++++--------------------------------
 talerbank/app/views.py      |  49 +++++---
 5 files changed, 120 insertions(+), 223 deletions(-)

diff --git a/requirements.txt b/requirements.txt
index 05f7b3a..840a0e8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,4 +2,3 @@ Django==1.9.5
 psycopg2==2.6.1
 requests==2.9.1
 uWSGI==2.0.12
-validictory==1.0.1
diff --git a/setup.py b/setup.py
index dfd41cc..1717d8e 100755
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,6 @@ setup(name='talerbank',
                         "psycopg2",
                         "requests",
                         "uWSGI",
-                        "validictory",
                         "mock",
                         "jinja2"
                         ],
diff --git a/talerbank/app/middleware.py b/talerbank/app/middleware.py
index dd1b3ac..d7c3775 100644
--- a/talerbank/app/middleware.py
+++ b/talerbank/app/middleware.py
@@ -6,9 +6,12 @@ from .models import BankAccount, BankTransaction
 from .views import \
     (DebitLimitException, SameAccountException,
      LoginFailed, RejectNoRightsException)
+
 from .schemas import \
-    (URLParameterMissing, URLParameterMalformed,
-     JSONFieldException, URLParamValidationError)
+    (JSONFieldException,
+     URLParamValidationError,
+     InvalidSession)
+
 from .amount import \
     (CurrencyMismatch, BadFormatAmount,
      NumberTooBig, NegativeNumber)
@@ -70,8 +73,10 @@ class ExceptionMiddleware:
             BankTransaction.DoesNotExist: 1,
             SameAccountException: 2,
             DebitLimitException: 3,
-            URLParameterMissing: 8,
-            URLParameterMalformed: 9,
+
+            ##
+            # FIXME: needs own error code.
+            InvalidSession: 0,
 
             ##
             # This one unified class kills the distinction
diff --git a/talerbank/app/schemas.py b/talerbank/app/schemas.py
index d44a03b..37094b4 100644
--- a/talerbank/app/schemas.py
+++ b/talerbank/app/schemas.py
@@ -20,14 +20,11 @@
 #  @brief definitions of JSON schemas for validating data
 
 import json
-from validictory import validate
-from validictory.validator import \
-    (RequiredFieldValidationError,
-     FieldValidationError)
 from django.conf import settings
 from django.core.exceptions import ValidationError
 from django import forms
 from django.core.validators import RegexValidator
+from urllib.parse import urlparse
 
 
 ##
@@ -44,10 +41,41 @@ UINT64_MAX = (2**64) - 1
 AMOUNT_REGEX = "^[A-Za-z0-9_-]+:([0-9]+)\.?([0-9]+)?$"
 
 ##
+# Exception class to be raised when a expected URL parameter
+# is not found.
+class InvalidSession(ValueError):
+    ##
+    # Init method.
+    #
+    # @param self the object itself.
+    # @param http_status_code the HTTP response code to return
+    #        to the caller (client).
+    def __init__(self, http_status_code):
+        self.hint = "Landed on a broken session"
+        self.http_status_code = http_status_code
+        super().__init__()
+
+##
+# Exception class to be raised when a JSON
+# object does not respect a specification.
+class JSONFieldException(ValueError):
+
+    ##
+    # Init method.
+    #
+    # @param self the object itself.
+    # @param error object containing the hint.
+    # @param http_status_code the HTTP response code to return
+    #        to the caller (client).
+    def __init__(self, error, http_status_code):
+        self.hint = json.dumps(error.as_json())
+        self.http_status_code = http_status_code
+        super().__init__()
+
+##
 # Exception class to be raised when at least one expected URL
 # parameter is either not found or malformed.
-class URLParamValidationError(ValidationError):
-
+class URLParamValidationError(ValueError):
     ##
     # Init method.
     #
@@ -56,12 +84,11 @@ class URLParamValidationError(ValidationError):
     # @param http_status_code the HTTP response code to return
     #        to the caller (client).
     def __init__(self, error, http_status_code):
-        self.hint = ["%s: %s, " % (k, error[k]) for k in error]
+        self.hint = json.stringify(error.as_json())
         self.http_status_code = http_status_code
         super().__init__()
 
 class AuthForm(forms.Form):
-
     type = forms.CharField(
         validators=[RegexValidator(
             "^basic$",
@@ -131,7 +158,6 @@ class HistoryParamsBase(forms.Form):
     # FIXME: adjust min/max values.
     account_number = forms.IntegerField(required=False)
 
-
 class HistoryParams(HistoryParamsBase):
     
     def clean_start(self):
@@ -150,203 +176,48 @@ class HistoryRangeParams(HistoryParamsBase):
     end = forms.IntegerField()
     start = forms.IntegerField()
 
-##
-# Exception class to be raised when a expected URL parameter
-# is not found.
-class URLParameterMissing(ValueError):
-
-    ##
-    # Init method.
-    #
-    # @param self the object itself.
-    # @param param the missing URL parameter name.
-    # @param http_status_code the HTTP response code to return
-    #        to the caller (client).
-    def __init__(self, param, http_status_code):
-        self.hint = "URL parameter '%s' is missing" % param
-        self.http_status_code = http_status_code
-        super().__init__()
-
-##
-# Exception class to be raised when a expected URL parameter
-# is malformed.
-class URLParameterMalformed(ValueError):
-
-    ##
-    # Init method.
-    #
-    # @param self the object itself.
-    # @param param the malformed URL parameter name.
-    # @param http_status_code the HTTP response code to return
-    #        to the caller (client).
-    def __init__(self, param, http_status_code):
-        self.hint = "URL parameter '%s' is malformed" % param
-        self.http_status_code = http_status_code
-        super().__init__()
-
-
-##
-# Exception class to be raised when a JSON
-# object does not respect a specification.
-class JSONFieldException(ValueError):
-
-    ##
-    # Init method.
-    #
-    # @param self the object itself.
-    # @param error object containing the hint.
-    # @param http_status_code the HTTP response code to return
-    #        to the caller (client).
-    def __init__(self, error, http_status_code):
-        self.hint = json.dumps(error.as_json())
-        self.http_status_code = http_status_code
-        super().__init__()
-
-##
-# Pattern for amounts.
-AMOUNT_SCHEMA = {
-    "type": "string",
-    "pattern": "^[A-Za-z0-9_-]+:([0-9]+)\.?([0-9]+)?$"}
-
-##
-# Definition that withdraw request bodies have to match.
-WITHDRAW_SESSION_SCHEMA = {
-    "type": "object",
-    "properties": {
-        "amount": {"type": AMOUNT_SCHEMA},
-        "reserve_pub": {"type": "string"},
-        "exchange_account_number": {"type": "integer"},
-        "sender_wiredetails": {
-            "type": "object",
-            "properties": {
-                "type": {"type": "string"},
-                "bank_url": {"type": "string"},
-                "account_number": {"type": "integer"}
-            }
-        }
-    }
-}
-
-##
-# Definition for wire details.
-WIREDETAILS_SCHEMA = {
-    "type": "object",
-    "properties": {
-        "test": {
-            "type": "object",
-            "properties": {
-                "type": {"type": "string"},
-                "account_number": {"type": "integer"},
-                "bank_url": {"type": "string"},
-                "name": {"type": "string", "required": False},
-            }
-        }
-    }
-}
-
-##
-# Definition for authentication objects.
-AUTH_SCHEMA = {
-    "type": "object",
-    "properties": {
-        "type": {"type": "string",
-                 "pattern": "^basic$"},
-        "data": {"type": "object", "required": False}
-    }
-}
-
-
-##
-# Definition for PIN/TAN request URL parameters.
-PIN_TAN_ARGS = {
-    "type": "object",
-    "properties": {
-        "amount_value": {"format": "str_to_int"},
-        "amount_fraction": {"format": "str_to_int"},
-        "amount_currency": {"type": "string"},
-        "exchange": {"type": "string"},
-        "reserve_pub": {"type": "string"},
-        "exchange_wire_details": {"format": "string"}
-    }
-}
-
-##
-# Check the custom types for a PIN/TAN request.  Those
-# types the strings that must represent integers (like
-# those used for amount's values and fractions), and the
-# stringification of a wire details object.  All of them
-# get passed along the GET request's arguments by the
-# wallet.
-#
-# @param validator unused.
-# @param fieldname current name of the field being processed.
-# @param value current name of the value being processed.
-# @param format_option holds the format definition for the
-#        current field being processed.
-def validate_pintan_types(validator, fieldname, value, format_option):
-    del validator # pacify PEP checkers
-    try:
-        if format_option == "str_to_int":
-            int(value)
-        if format_option == "wiredetails_string":
-            data = json.loads(value)
-            validate_wiredetails(data)
-    except Exception:
-        raise FieldValidationError(
-            "Malformed '%s'" % fieldname, fieldname, value)
-
-##
-# Check the GET arguments given along a /pin/question request.
-#
-# @param data GET arguments in a dict.
-def validate_pin_tan(data):
-    format_dict = {
-        "str_to_int": validate_pintan_types,
-        "wiredetails_string": validate_pintan_types}
-    validate(data, PIN_TAN_ARGS, format_validators=format_dict)
+class PaytoField(forms.Field):
+    # TODO: try removing this method.
+    def to_python(self, value):
+        return value
 
-##
-# Check wire details
-# (regardless of which endpoint triggered the check)
-#
-# @param wiredetails object representing wire details.
-def validate_wiredetails(wiredetails):
-    validate(wiredetails, WIREDETAILS_SCHEMA)
+    def validate(self, value):
+        wire_uri = urlparse(value)
+        if "payto" != wire_uri.scheme:
+            raise ValidationError("URL is not 'payto'")
 
-##
-# Check that the state corresponds to a withdrawal session.
-#
-# @param data the dict representing the server state.  So not
-#        strictly an 'input' sent by any client; we use this just
-#        as a double-checking mechanism to see if the server is
-#        well-behaved.
-def check_withdraw_session(data):
-    validate(data, WITHDRAW_SESSION_SCHEMA)
+class PinTanParams(forms.Form):
+    amount_currency = forms.CharField(
+        validators=[RegexValidator(
+            "^[A-Z]+$",
+            message="Currency is all upper case")])
+    amount_value = forms.IntegerField(min_value=0)
+    amount_fraction = forms.IntegerField(min_value=0)
+    exchange = forms.URLField()
+    reserve_pub = forms.CharField()
+    exchange_wire_details = PaytoField()
+
+class SenderWireDetails(forms.Form):
+    # FIXME: must be changed to 'payto' format.
+    type = forms.CharField()
+    bank_url = forms.URLField()
+    account_number = forms.IntegerField(min_value=1)
+
+class SenderWireDetailsField(forms.Field):
+    def to_python(self, value):
+        return value
+    def validate(self, value):
+        swd = SenderWireDetails(value)
+        if not swd.is_valid():
+            raise ValidationError(
+                json.dumps(swd.errors.as_json()))
 
+class WithdrawSessionData(forms.Form):
+    amount = forms.CharField(validators=[RegexValidator(
+        AMOUNT_REGEX,
+        message="Could not find valid amount in state..")])
+    reserve_pub = forms.CharField()
+    exchange_account_number = forms.IntegerField(min_value=1)
+    sender_wiredetails = SenderWireDetailsField()
 
-##
-# Abstraction over the real validators.  Do not return,
-# but rather raise exceptions if the data is invalid.
-#
-# @param request Django-specific HTTP request object.
-# @param data data to validate.  May be a POST body or
-#        a dict holding the param-value pairs from a GET
-#        request.
-def validate_data(request, data):
-    switch = {
-        "/pin/verify": check_withdraw_session,
-        "/pin/question": validate_pin_tan
-    }
 
-    try:
-        switch.get(request.path_info)(data)
-    except  RequiredFieldValidationError as exc:
-        if request.method == "GET":
-            raise URLParameterMissing(exc.fieldname, 400)
-        raise JSONFieldException(
-            "Field '%s' is missing" % exc.fieldname, 400)
-    except FieldValidationError as exc:
-        if request.method == "GET":
-            raise URLParameterMalformed(exc.fieldname, 400)
-        raise JSONFieldException(
-            "Malformed '%s' field" % exc.fieldname, 400)
diff --git a/talerbank/app/views.py b/talerbank/app/views.py
index 88e5742..550f658 100644
--- a/talerbank/app/views.py
+++ b/talerbank/app/views.py
@@ -44,7 +44,12 @@ from django.shortcuts import render, redirect
 from datetime import datetime
 from .models import BankAccount, BankTransaction
 from .amount import Amount
-from .schemas import validate_data, HistoryParams, HistoryRangeParams, 
URLParamValidationError, RejectData, AddIncomingData, JSONFieldException
+from .schemas import \
+    (HistoryParams, HistoryRangeParams,
+     URLParamValidationError, RejectData,
+     AddIncomingData, JSONFieldException,
+     PinTanParams, InvalidSession,
+     WithdrawSessionData)
 
 LOGGER = logging.getLogger(__name__)
 
@@ -327,23 +332,34 @@ def get_acct_from_payto(uri_str: str) -> int:
 @require_GET
 @login_required
 def pin_tan_question(request):
-    validate_data(request, request.GET.dict())
+    
+    get_params = PinTanParams(request.GET.dict())
+    if not get_params.is_valid():
+        raise URLParamValidationError(get_params.errors, 400)
+
+
     user_account = BankAccount.objects.get(user=request.user)
-    wire_details = request.GET["exchange_wire_details"]
+    wire_details = get_params.cleaned_data["exchange_wire_details"]
 
-    request.session["exchange_account_number"] = 
get_acct_from_payto(wire_details)
-    amount = Amount(request.GET["amount_currency"],
-                    int(request.GET["amount_value"]),
-                    int(request.GET["amount_fraction"]))
+    request.session["exchange_account_number"] = \
+        get_acct_from_payto(wire_details)
+    amount = Amount(get_params.cleaned_data["amount_currency"],
+                    get_params.cleaned_data["amount_value"],
+                    get_params.cleaned_data["amount_fraction"])
     request.session["amount"] = amount.dump()
-    request.session["reserve_pub"] = request.GET["reserve_pub"]
-    fail_message, success_message, hint = get_session_hint(request, 
"captcha_failed")
+    request.session["reserve_pub"] = \
+        get_params.cleaned_data["reserve_pub"]
+
+    fail_message, success_message, hint = get_session_hint(
+        request,
+        "captcha_failed")
+
     question, hashed_answer = make_question()
     context = dict(
         question=question,
         hashed_answer=hashed_answer,
         amount=amount.stringify(settings.TALER_DIGITS),
-        exchange=request.GET["exchange"],
+        exchange=get_params.cleaned_data["exchange"],
         fail_message=fail_message,
         success_message=success_message,
         hint=hint)
@@ -367,7 +383,14 @@ def pin_tan_verify(request):
         request.session["captcha_failed"] = True, False, "Wrong CAPTCHA 
answer."
         return redirect(request.POST.get("question_url", "profile"))
     # Check the session is a "pin tan" one
-    validate_data(request, request.session)
+
+    if not WithdrawSessionData(request.session):
+        # The session is not valid: either because the client simply
+        # requested the page without passing through the prior step,
+        # or because the bank broke it in the meanwhile.  Let's blame
+        # ourselves for now.
+        raise InvalidSession(503)
+
     amount = Amount(**request.session["amount"])
     exchange_bank_account = BankAccount.objects.get(
         account_no=request.session["exchange_account_number"])
@@ -885,11 +908,11 @@ 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.
+#        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.
 # @return a @a BankTransaction object.
-def wire_transfer(amount, debit_account, credit_account,
-                  subject):
+def wire_transfer(amount, debit_account, credit_account, subject):
     LOGGER.debug("%s => %s, %s, %s" %
                  (debit_account.account_no,
                   credit_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]