## # This file is part of TALER # (C) 2014, 2015, 2016 Taler Systems SA # # TALER is free software; you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as # published by the Free Software Foundation; either version 3, or # (at your option) any later version. TALER is distributed in the # hope that it will be useful, but WITHOUT ANY WARRANTY; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public # License along with TALER; see the file COPYING. If not, see # # # @author Marcello Stanisci # @author Florian Dold from functools import wraps import math import json import logging import hashlib import random import re from urllib.parse import urlparse import django.contrib.auth import django.contrib.auth.views import django.contrib.auth.forms from django.db import transaction from django import forms from django.conf import settings from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST, require_GET from django.views.decorators.http import require_http_methods from django.urls import reverse from django.contrib.auth.models import User from django.db.models import Q from django.http import JsonResponse, HttpResponse from django.shortcuts import render, redirect from django.core.exceptions import ObjectDoesNotExist from datetime import datetime from .models import BankAccount, BankTransaction, TalerWithdrawOperation from taler.util.amount import Amount import qrcode import qrcode.image.svg import lxml from .schemas import ( HistoryParams, HistoryRangeParams, URLParamValidationError, RejectData, AddIncomingData, JSONFieldException, InvalidSession, WithdrawHeadless, WithdrawHeadlessUri ) 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 ## # Exception raised upon failing login. # class LoginFailed(Exception): def __init__(self, msg): super(LoginFailed, self).__init__(msg) self.hint = "Wrong password given" 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.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.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. class PrivateAccountException(Exception): def __init__(self, msg): super(PrivateAccountException, self).__init__(msg) self.hint = "Cannot show history from private persons accounts" self.http_status_code = 402 ## # Exception raised when some financial operation goes # beyond the limit threshold. class DebitLimitException(Exception): def __init__(self, msg): super(DebitLimitException, self).__init__(msg) self.hint = "Payment aborted for insufficient credit" self.http_status_code = 406 self.taler_error_code = 5103 ## # Exception raised when some financial operation is # attempted and both parties are the same account number. # class SameAccountException(Exception): def __init__(self, msg): super(SameAccountException, self).__init__(msg) self.hint = "Cannot send payment to oneself." self.http_status_code = 403 self.taler_error_code = 5102 ## # Exception raised when someone tries to reject a # transaction, but they have no rights to accomplish # such operation. class RejectNoRightsException(Exception): def __init__(self, msg): super(RejectNoRightsException, self).__init__(msg) self.hint = "Only original payer can reject." 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) self.hint = msg self.http_status_code = 500 self.taler_error_code = 5300 ## # The authentication for users to log in the bank. # class TalerAuthenticationForm(django.contrib.auth.forms.AuthenticationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["username"].widget.attrs["autofocus"] = True self.fields["username"].widget.attrs["placeholder"] = "Username" self.fields["password"].widget.attrs["placeholder"] = "Password" ## # Return a empty response. Used in "/favicon.ico" requests. # def ignore(request): del request return HttpResponse() ## # Decode body, when it is expected to be UTF-8. # # @param request the HTTP request being served. # @return the body as string. def decode_body(request): return request.body.decode("utf-8") ## # Get a flag from the session and clear it. # # @param request the HTTP request being served. # @param name name of the session value that should be retrieved. # @return the value, if found; otherwise False. def get_session_flag(request, name): if name in request.session: ret = request.session[name] del request.session[name] return ret return False ## # A session "hint" is a tuple indicating whether the # message is for a failure or a success, and containing # the message itself. # # @param request the HTTP request being served. # @param name hint name # @return the hint (a "null" one if none was found) def get_session_hint(request, hintId): if hintId in request.session: ret = request.session[hintId] del request.session[hintId] return ret return False, False, None def set_profile_hint(request, *, success, failure, 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: LOGGER.warning(f"Overriding a non consumed hint: {hintId}") del request.session[hintId] request.session[hintId] = success, failure, hint ## # Build the list containing all the predefined accounts; the # list contains, for example, the exchange, the bank itself, and # all the public accounts (like GNUnet / Tor / FSF / ..) def predefined_accounts_list(): account = 2 ret = [] for i in settings.TALER_PREDEFINED_ACCOUNTS[1:]: ret.append((account, "%s (#%d)" % (i, account))) account += 1 return ret ## # Thanks to [1], this class provides a dropdown menu that # can be used within a