diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-12-19 11:15:19 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-12-19 11:15:35 +0100 |
commit | 45bb124b81d94938483a932338137465e6876a91 (patch) | |
tree | 0820477333248ad1802b8b9ff86feb87e1c0c240 /talerbank/app | |
parent | 11ecfd15ffe16ba35dac54c3b8794bb51c2091df (diff) | |
download | bank-45bb124b81d94938483a932338137465e6876a91.tar.gz bank-45bb124b81d94938483a932338137465e6876a91.tar.bz2 bank-45bb124b81d94938483a932338137465e6876a91.zip |
formatting / debugging
Diffstat (limited to 'talerbank/app')
-rw-r--r-- | talerbank/app/views.py | 261 |
1 files changed, 122 insertions, 139 deletions
diff --git a/talerbank/app/views.py b/talerbank/app/views.py index a0a58b1..a8aaa4f 100644 --- a/talerbank/app/views.py +++ b/talerbank/app/views.py @@ -49,9 +49,15 @@ import qrcode import qrcode.image.svg import lxml from .schemas import ( - HistoryParams, HistoryRangeParams, URLParamValidationError, RejectData, - AddIncomingData, JSONFieldException, InvalidSession, WithdrawHeadless, - WithdrawHeadlessUri + HistoryParams, + HistoryRangeParams, + URLParamValidationError, + RejectData, + AddIncomingData, + JSONFieldException, + InvalidSession, + WithdrawHeadless, + WithdrawHeadlessUri, ) LOGGER = logging.getLogger(__name__) @@ -60,7 +66,7 @@ LOGGER = logging.getLogger(__name__) # Constant value for the biggest number the bank handles. # This value is just equal to the biggest number that JavaScript # can handle (because of the wallet). -UINT64_MAX = (2**64) - 1 +UINT64_MAX = (2 ** 64) - 1 ## # Exception raised upon failing login. @@ -72,20 +78,23 @@ class LoginFailed(Exception): self.http_status_code = 401 self.taler_error_code = 5109 + class InvalidInputData(Exception): def __init__(self, msg): super(InvalidInputData, self).__init__(msg) - self.hint = msg # should mention the picked username + self.hint = msg # should mention the picked username self.http_status_code = 400 self.taler_error_code = 5400 + class UsernameUnavailable(Exception): def __init__(self, msg): super(UsernameUnavailable, self).__init__(msg) - self.hint = msg # should mention the picked username + self.hint = msg # should mention the picked username self.http_status_code = 406 self.taler_error_code = 5400 + ## # Exception raised when the public history from # a ordinary user account is tried to be accessed. @@ -130,6 +139,7 @@ class RejectNoRightsException(Exception): self.http_status_code = 403 self.taler_error_code = 5200 + class UnhandledException(Exception): def __init__(self, msg="Unhandled exception happened!"): super(UnhandledException, self).__init__(msg) @@ -137,6 +147,7 @@ class UnhandledException(Exception): self.http_status_code = 500 self.taler_error_code = 5300 + ## # The authentication for users to log in the bank. # @@ -164,6 +175,7 @@ def ignore(request): def decode_body(request): return request.body.decode("utf-8") + ## # Get a flag from the session and clear it. # @@ -194,8 +206,12 @@ def get_session_hint(request, hintId): return False, False, None + def set_profile_hint(request, *, success, failure, hint): - set_session_hint(request, "profile_hint", success=success, failure=failure, hint=hint) + set_session_hint( + request, "profile_hint", success=success, failure=failure, hint=hint + ) + def set_session_hint(request, hintId, *, success, failure, hint): if hintId in request.session: @@ -256,8 +272,7 @@ class InputDatalist(forms.TextInput): html = super().render(name, value, attrs=attrs, renderer=renderer) datalist = '<datalist id="%slist">' % self._name for dl_value, dl_text in self._datalist: - datalist += '<option value="%s">%s</option>' \ - % (dl_value, dl_text) + datalist += '<option value="%s">%s</option>' % (dl_value, dl_text) datalist += "</datalist>" return html + datalist @@ -268,8 +283,7 @@ class InputDatalist(forms.TextInput): # class WTForm(forms.Form): amount = forms.FloatField( - min_value=0.1, - widget=forms.NumberInput(attrs={"class": "currency-input"}) + min_value=0.1, widget=forms.NumberInput(attrs={"class": "currency-input"}) ) receiver = forms.IntegerField( min_value=1, widget=InputDatalist(predefined_accounts_list, "receiver") @@ -298,21 +312,18 @@ def profile_page(request): wtf = WTForm(request.POST) if wtf.is_valid(): amount_parts = ( - settings.TALER_CURRENCY, wtf.cleaned_data.get("amount") + 0.0 + settings.TALER_CURRENCY, + wtf.cleaned_data.get("amount") + 0.0, ) wire_transfer( Amount.parse("%s:%s" % amount_parts), BankAccount.objects.get(user=request.user), - BankAccount.objects.get( - account_no=wtf.cleaned_data.get("receiver") - ), wtf.cleaned_data.get("subject") + BankAccount.objects.get(account_no=wtf.cleaned_data.get("receiver")), + wtf.cleaned_data.get("subject"), ) set_profile_hint( - request, - failure=False, - success=True, - hint="Wire transfer successful!" + request, failure=False, success=True, hint="Wire transfer successful!" ) return redirect("profile") @@ -330,9 +341,7 @@ def profile_page(request): currency=request.user.bankaccount.amount.currency, account_no=request.user.bankaccount.account_no, wt_form=wtf, - history=extract_history( - request.user.bankaccount, -1 * (UINT64_MAX / 2 / 2) - ) + history=extract_history(request.user.bankaccount, -1 * (UINT64_MAX / 2 / 2)), ) if settings.TALER_SUGGESTED_EXCHANGE: context["suggested_exchange"] = settings.TALER_SUGGESTED_EXCHANGE @@ -397,8 +406,7 @@ def internal_register(request): input_data = UserReg(request.POST) if not input_data.is_valid(): - msg = "Wrong field(s): %s." % \ - ", ".join(input_data.errors.keys()) + msg = "Wrong field(s): %s." % ", ".join(input_data.errors.keys()) raise InvalidInputData(msg) username = input_data.cleaned_data["username"] @@ -409,9 +417,7 @@ def internal_register(request): # Registration goes through. with transaction.atomic(): - user = User.objects.create_user( - username=username, - password=password) + user = User.objects.create_user(username=username, password=password) user_account = BankAccount(user=user) user_account.save() bank_internal_account = BankAccount.objects.get(account_no=1) @@ -429,7 +435,8 @@ def internal_register(request): wire_transfer( Amount(settings.TALER_CURRENCY, 100, 0), bank_internal_account, - user_account, "Joining bonus" + user_account, + "Joining bonus", ) return user @@ -479,25 +486,23 @@ def register(request): except InvalidInputData as e: return render( - request, "register.html", { + request, + "register.html", + { "wrong": True, - "hint": "Wrong field(s): %s." % ", ".join(form.errors.keys()) - } + "hint": "Wrong field(s): %s." % ", ".join(form.errors.keys()), + }, ) except DebitLimitException as e: return render( - request, "register.html", { - "wrong": True, - "hint": "Out of business, cannot admit new customers." - } + request, + "register.html", + {"wrong": True, "hint": "Out of business, cannot admit new customers."}, ) set_profile_hint( - request, - success=True, - failure=False, - hint="Registration successful!" + request, success=True, failure=False, hint="Registration successful!" ) django.contrib.auth.login(request, user) @@ -545,7 +550,7 @@ def extract_history(account, delta, start=None): counterpart=counterpart.account_no, counterpart_username=counterpart.user.username, subject=item.subject, - date=item.date.strftime("%d/%m/%y %H:%M") + date=item.date.strftime("%d/%m/%y %H:%M"), ) history.append(entry) return history @@ -583,7 +588,7 @@ def serve_public_accounts(request, name=None, page=None): # and django/python is not allowing slicing with big numbers. UINT64_MAX / 2 / 2, 0, - "descending" + "descending", ).count() DELTA = 30 # '//' operator is NO floating point. @@ -592,8 +597,9 @@ def serve_public_accounts(request, name=None, page=None): public_accounts = BankAccount.objects.filter(is_public=True) # Retrieve DELTA records younger than 'start_row' (included). - history = extract_history(user.bankaccount, DELTA * page, - 0)[DELTA * (page - 1):(DELTA * page)] + history = extract_history(user.bankaccount, DELTA * page, 0)[ + DELTA * (page - 1) : (DELTA * page) + ] pages = list(range(1, num_pages + 1)) @@ -603,11 +609,9 @@ def serve_public_accounts(request, name=None, page=None): forth=page + 1 if page < num_pages else None, public_accounts=public_accounts, selected_account=dict( - name=name, - number=user.bankaccount.account_no, - history=history, + name=name, number=user.bankaccount.account_no, history=history, ), - pages=pages + pages=pages, ) return render(request, "public_accounts.html", context) @@ -640,8 +644,7 @@ def login_via_headers(view_func): # is called. def direction_switch(bank_account, direction): direction_switch = { - "both": - (Q(debit_account=bank_account) | Q(credit_account=bank_account)), + "both": (Q(debit_account=bank_account) | Q(credit_account=bank_account)), "credit": Q(credit_account=bank_account), "debit": Q(debit_account=bank_account), "cancel+": (Q(credit_account=bank_account) & Q(cancelled=True)), @@ -673,8 +676,7 @@ def direction_switch(bank_account, direction): # youngest entry in the first position. def query_history_range(bank_account, direction, start, end, descending): qs = BankTransaction.objects.filter( - direction_switch(bank_account, direction), Q(date__gte=start), - Q(date__lte=end) + direction_switch(bank_account, direction), Q(date__gte=start), Q(date__lte=end) ) order = "-id" if descending else "id" @@ -720,7 +722,7 @@ def query_history(bank_account, direction, delta, start, ordering): direction_switch(bank_account, direction), sign_filter ) order = "-id" if "descending" == ordering else "id" - return qs.order_by(order)[:abs(delta)] + return qs.order_by(order)[: abs(delta)] ## @@ -740,8 +742,7 @@ def build_history_response(qs, cancelled, user_account): sign_ = "-" if entry.cancelled and cancelled == "omit": continue - if entry.credit_account.account_no == \ - user_account.bankaccount.account_no: + if entry.credit_account.account_no == user_account.bankaccount.account_no: counterpart = entry.debit_account.account_no sign_ = "+" cancel = "cancel" if entry.cancelled else "" @@ -753,7 +754,7 @@ def build_history_response(qs, cancelled, user_account): sign=sign_, wt_subject=entry.subject, row_id=entry.id, - date="/Date(" + str(int(entry.date.timestamp())) + ")/" + date="/Date(" + str(int(entry.date.timestamp())) + ")/", ) ) return history @@ -779,12 +780,10 @@ def serve_history_range(request, user_account): args.get("direction", "both"), start_td, end_td, - args.get("ordering") + args.get("ordering"), ) - history = build_history_response( - qs, args.get("cancelled", "show"), user_account - ) + history = build_history_response(qs, args.get("cancelled", "show"), user_account) if not history: return HttpResponse(status=204) @@ -806,12 +805,10 @@ def serve_history(request, user_account): args.get("direction"), args.get("delta"), args.get("start", None), - args.get("ordering", "descending") + args.get("ordering", "descending"), ) - history = build_history_response( - qs, args.get("cancelled", "show"), user_account - ) + history = build_history_response(qs, args.get("cancelled", "show"), user_account) if not history: return HttpResponse(status=204) @@ -833,9 +830,7 @@ def auth_and_login(request): password = request.META.get("HTTP_X_TALER_BANK_PASSWORD") if not username or not password: raise LoginFailed("missing user/password") - return django.contrib.auth.authenticate( - username=username, password=password - ) + return django.contrib.auth.authenticate(username=username, password=password) ## @@ -855,8 +850,7 @@ def reject(request, user_account): data = RejectData(json.loads(decode_body(request))) trans = BankTransaction.objects.get(id=data.get("row_id")) - if trans.credit_account.account_no != \ - user_account.bankaccount.account_no: + if trans.credit_account.account_no != user_account.bankaccount.account_no: raise RejectNoRightsException() trans.cancelled = True if trans.debit_account.debit: @@ -865,11 +859,11 @@ def reject(request, user_account): # debit_account.amount <= trans.amount trans.debit_account.debit = False tmp = Amount(**trans.amount.dump()) - tmp.subtract (trans.debit_account.amount) - trans.debit_account.amount.set (**tmp.dump()) + tmp.subtract(trans.debit_account.amount) + trans.debit_account.amount.set(**tmp.dump()) else: # debit_account > trans.amount - trans.debit_account.amount.subtract (trans.amount) + trans.debit_account.amount.subtract(trans.amount) else: # balance is positive, simply add trans.debit_account.amount.add(trans.amount) @@ -881,8 +875,8 @@ def reject(request, user_account): # credit_account.amount < trans.amount trans.credit_account.debit = True tmp = Amount(**trans.amount.dump()) - tmp.subtract (trans.credit_account.amount) - trans.credit_account.amount.set (**tmp.dump()) + tmp.subtract(trans.credit_account.amount) + trans.credit_account.amount.set(**tmp.dump()) else: # credit_account.amount >= trans.amount trans.credit_account.amount.subtract(trans.amount) @@ -906,24 +900,20 @@ def add_incoming(request, user_account): data = AddIncomingData(json.loads(decode_body(request))) - subject = "%s %s" % ( - data.get("subject"), data.get("exchange_url") - ) + subject = "%s %s" % (data.get("subject"), data.get("exchange_url")) - credit_account = BankAccount.objects.get( - account_no=data.get("credit_account") - ) + credit_account = BankAccount.objects.get(account_no=data.get("credit_account")) wtrans = wire_transfer( - Amount.parse(data.get("amount")), user_account.bankaccount, - credit_account, subject + Amount.parse(data.get("amount")), + user_account.bankaccount, + credit_account, + subject, ) - return JsonResponse({ - "row_id": wtrans.id, - "timestamp": "/Date(%s)/" % int(wtrans.date.timestamp()) - }) - + return JsonResponse( + {"row_id": wtrans.id, "timestamp": "/Date(%s)/" % int(wtrans.date.timestamp())} + ) @login_via_headers @@ -937,9 +927,8 @@ def withdraw_headless_uri(request, user): op.save() host = request.get_host() taler_withdraw_uri = f"taler://withdraw/{host}/-/{op.withdraw_id}" - return JsonResponse({ - "taler_withdraw_uri": taler_withdraw_uri, - }) + return JsonResponse({"taler_withdraw_uri": taler_withdraw_uri,}) + ## # Serves a headless withdrawal request for the Taler protocol. @@ -950,22 +939,20 @@ def withdraw_headless_uri(request, user): @csrf_exempt @require_POST def withdraw_headless(request, user): - + data = WithdrawHeadless(json.loads(decode_body(request))) - sender_payto = "payto://x-taler-bank/%s/%d" % \ - (request.get_host(), user.bankaccount.account_no) - ret_obj = ({"sender_wire_details": sender_payto}) + sender_payto = "payto://x-taler-bank/%s/%d" % ( + request.get_host(), + user.bankaccount.account_no, + ) + ret_obj = {"sender_wire_details": sender_payto} exchange_payto = data.get("exchange_wire_details") if not exchange_payto: - exchange_accno = get_acct_from_payto( - settings.TALER_SUGGESTED_EXCHANGE_PAYTO - ) + 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( - exchange_payto - ) + exchange_accno = get_acct_from_payto(exchange_payto) exchange_bankaccount = BankAccount.objects.get(account_no=exchange_accno) @@ -973,7 +960,7 @@ def withdraw_headless(request, user): Amount.parse(data.get("amount")), user.bankaccount, exchange_bankaccount, - data.get("reserve_pub") + data.get("reserve_pub"), ) return JsonResponse(ret_obj) @@ -986,9 +973,7 @@ def api_withdraw_operation(request, withdraw_id): try: op = TalerWithdrawOperation.objects.get(withdraw_id=withdraw_id) except ObjectDoesNotExist: - return JsonResponse( - dict(error="withdraw operation does not exist"), status=404 - ) + return JsonResponse(dict(error="withdraw operation does not exist"), status=404) user_acct_no = op.withdraw_account.account_no host = request.get_host() @@ -998,9 +983,7 @@ def api_withdraw_operation(request, withdraw_id): try: account_no = get_acct_from_payto(exchange_payto_uri) except: - return JsonResponse( - dict(error="exchange payto URI malformed"), status=400 - ) + return JsonResponse(dict(error="exchange payto URI malformed"), status=400) try: exchange_acct = BankAccount.objects.get(account_no=account_no) except ObjectDoesNotExist: @@ -1009,9 +992,7 @@ def api_withdraw_operation(request, withdraw_id): ) selected_reserve_pub = data.get("reserve_pub") if not isinstance(selected_reserve_pub, str): - return JsonResponse( - dict(error="reserve_pub must be a string"), status=400 - ) + return JsonResponse(dict(error="reserve_pub must be a string"), status=400) if op.selection_done or op.withdraw_done: if ( op.selected_exchange_account != exchange_acct @@ -1019,7 +1000,7 @@ def api_withdraw_operation(request, withdraw_id): ): return JsonResponse( dict(error="selection of withdraw parameters already done"), - status=409 + status=409, ) # No conflict, same data! return JsonResponse(dict(), status=200) @@ -1038,14 +1019,12 @@ def api_withdraw_operation(request, withdraw_id): sender_wire=f"payto://x-taler-bank/{host}/{user_acct_no}", suggested_exchange=settings.TALER_SUGGESTED_EXCHANGE, confirm_transfer_url=request.build_absolute_uri( - reverse("withdraw-confirm", args=(withdraw_id, )) - ) + reverse("withdraw-confirm", args=(withdraw_id,)) + ), ) ) else: - return JsonResponse( - dict(error="only GET and POST are allowed"), status=305 - ) + return JsonResponse(dict(error="only GET and POST are allowed"), status=305) ## @@ -1097,9 +1076,7 @@ def show_withdrawal(request, withdraw_id): def confirm_withdrawal(request, withdraw_id): op = TalerWithdrawOperation.objects.get(withdraw_id=withdraw_id) if not op.selection_done: - raise Exception( - "invalid state (withdrawal parameter selection not done)" - ) + raise Exception("invalid state (withdrawal parameter selection not done)") if op.withdraw_done: return redirect("profile") if request.method == "POST": @@ -1107,24 +1084,23 @@ def confirm_withdrawal(request, withdraw_id): hashed_solution = request.POST.get("pin_1", "") if hashed_attempt != hashed_solution: LOGGER.warning( - "Wrong CAPTCHA answer: %s vs %s", type(hashed_attempt), - type(request.POST.get("pin_1")) + "Wrong CAPTCHA answer: %s vs %s", + type(hashed_attempt), + type(request.POST.get("pin_1")), ) - request.session["captcha_failed" - ] = True, False, "Wrong CAPTCHA answer." + request.session["captcha_failed"] = True, False, "Wrong CAPTCHA answer." return redirect("withdraw-confirm", withdraw_id=withdraw_id) op.withdraw_done = True op.save() wire_transfer( - op.amount, BankAccount.objects.get(user=request.user), - op.selected_exchange_account, op.selected_reserve_pub + op.amount, + BankAccount.objects.get(user=request.user), + op.selected_exchange_account, + op.selected_reserve_pub, ) - + set_profile_hint( - request, - success=True, - failure=False, - hint="Withdrawal successful!" + request, success=True, failure=False, hint="Withdrawal successful!" ) request.session["just_withdrawn"] = True @@ -1137,7 +1113,7 @@ def confirm_withdrawal(request, withdraw_id): hashed_answer=hashed_answer, withdraw_id=withdraw_id, amount=op.amount.stringify(settings.TALER_DIGITS), - exchange=op.selected_exchange_account.user + exchange=op.selected_exchange_account.user, ) return render(request, "withdraw_confirm.html", context) raise Exception("not reached") @@ -1153,9 +1129,12 @@ def confirm_withdrawal(request, withdraw_id): # @return a @a BankTransaction object. def wire_transfer(amount, debit_account, credit_account, subject): LOGGER.debug( - "%s => %s, %s, %s" % ( - debit_account.account_no, credit_account.account_no, - amount.stringify(2), subject + "%s => %s, %s, %s" + % ( + debit_account.account_no, + credit_account.account_no, + amount.stringify(2), + subject, ) ) if debit_account.pk == credit_account.pk: @@ -1166,7 +1145,7 @@ def wire_transfer(amount, debit_account, credit_account, subject): amount=amount, credit_account=credit_account, debit_account=debit_account, - subject=subject + subject=subject, ) if debit_account.debit: @@ -1196,12 +1175,16 @@ def wire_transfer(amount, debit_account, credit_account, subject): threshold = Amount.parse(settings.TALER_MAX_DEBT) if debit_account.user.username == "Bank": threshold = Amount.parse(settings.TALER_MAX_DEBT_BANK) - if Amount.cmp(debit_account.amount, threshold) == 1 \ - and Amount.cmp(Amount(settings.TALER_CURRENCY), - threshold) != 0 \ - and debit_account.debit: + + LOGGER.info( + f"Account {debit_account.user.username} " + + f"(bal {debit_account.amount.stringify()}, thr {threshold.stringify()} requested transfer of" + + f"{threshold.stringify()} to account {credit_account.user.username}" + ) + + if Amount.cmp(debit_account.amount, threshold) > 0 and debit_account.debit: raise DebitLimitException( - f"Aborting payment initiated by '{debit_account.user.username}' for debit unallowed" + f"Aborting payment initiated by '{debit_account.user.username}', debit limit crossed." ) with transaction.atomic(): |