diff options
author | Marcello Stanisci <stanisci.m@gmail.com> | 2019-05-31 20:11:56 +0200 |
---|---|---|
committer | Marcello Stanisci <stanisci.m@gmail.com> | 2019-05-31 20:11:56 +0200 |
commit | c43180054aa9440916cf417cd9b123beaedfd078 (patch) | |
tree | 5b46f23f9b9e9125c6504a4667a3686666d5297c /talerbank/app | |
parent | 47d6ec42a49e9a87681d386c4a1dfac5733d7f90 (diff) | |
download | bank-c43180054aa9440916cf417cd9b123beaedfd078.tar.gz bank-c43180054aa9440916cf417cd9b123beaedfd078.tar.bz2 bank-c43180054aa9440916cf417cd9b123beaedfd078.zip |
5715.
Porting the withdraw-state checking logic,
and removig all the old Validictory data/functions.
Diffstat (limited to 'talerbank/app')
-rw-r--r-- | talerbank/app/middleware.py | 13 | ||||
-rw-r--r-- | talerbank/app/schemas.py | 196 | ||||
-rw-r--r-- | talerbank/app/views.py | 16 |
3 files changed, 76 insertions, 149 deletions
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 a826549..37094b4 100644 --- a/talerbank/app/schemas.py +++ b/talerbank/app/schemas.py @@ -20,10 +20,6 @@ # @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 @@ -45,6 +41,38 @@ 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(ValueError): @@ -56,12 +84,11 @@ class URLParamValidationError(ValueError): # @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$", @@ -170,144 +197,27 @@ class PinTanParams(forms.Form): reserve_pub = forms.CharField() exchange_wire_details = PaytoField() -## -# 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): +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) - ## - # 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} - } -} - -## -# 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 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, - } - 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 a5001e3..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, PinTanParams +from .schemas import \ + (HistoryParams, HistoryRangeParams, + URLParamValidationError, RejectData, + AddIncomingData, JSONFieldException, + PinTanParams, InvalidSession, + WithdrawSessionData) LOGGER = logging.getLogger(__name__) @@ -378,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"]) |