import logging import zlib from django.http import JsonResponse from django.shortcuts import redirect from .models import BankAccount, BankTransaction, \ BankAccountDoesNotExist, BankTransactionDoesNotExist from .views import \ (DebitLimitException, SameAccountException, LoginFailed, RejectNoRightsException) from .schemas import \ (JSONFieldException, URLParamValidationError, InvalidSession) from taler.util.amount import \ (CurrencyMismatch, BadFormatAmount, NumberTooBig, NegativeNumber) LOGGER = logging.getLogger() ## # Class decompressing requests. class DecompressionMiddleware: ## # Init constructor. # # @param self the object itself. # @param get_response a Django-provided callable that calls # whatever comes next in the chain: a further middleware # or the view itself (please refer to the official # documentation for more details). def __init__(self, get_response): self.get_response = get_response ## # This function is transparently invoked by Django when # a request traverses the chain made of middleware classes # and the view itself as the last element in the chain. # # Here happens the decompression. # # @param self this class. # @param request Django-specific request object (of the same # type that is handed to views). # @return Django-specific response object. def __call__(self, request): if "deflate" == request.META.get("HTTP_CONTENT_ENCODING"): request._body = zlib.decompress(request.body) return self.get_response(request) ## # Class holding data needed by the handling logic. class ExceptionMiddleware: ## # Init constructor. # # @param self the object itself. # @param get_response a Django-provided callable that calls # whatever comes next in the chain: a further middleware # or the view itself (please refer to the official # documentation for more details). def __init__(self, get_response): self.get_response = get_response # List of all the exceptions that are managed by # this module. self.excs = { BankAccountDoesNotExist: 0, BankTransactionDoesNotExist: 0, BankTransaction.DoesNotExist: 1, SameAccountException: 2, DebitLimitException: 3, ## # FIXME: needs own error code. InvalidSession: 0, ## # This one unified class kills the distinction # between a parameter being missing or malformed. # It simplifies the error handling though, and # MUST be reflected in taler_error_codes.h! URLParamValidationError: 9, JSONFieldException: 6, CurrencyMismatch: 11, BadFormatAmount: 11, LoginFailed: 12, RejectNoRightsException: 13, NumberTooBig: 1, NegativeNumber: 0 } # List of all the HTTP endpoint that are likely # to generate exceptions. self.apis = { "/withdraw": 5400, "/taler/withdraw": 5500, "/reject": 5300, "/history": 5200, "/admin/add/incoming": 5100 } # Map between endpoints and Web pages to render # after the exception gets managed. self.render = { "/profile": "profile", "/register": "index", "/public-accounts": "index", "/pin/verify": "profile", "/withdraw": "profile" } ## # This function is transparently invoked by Django when # a request traverses the chain made of middleware classes # and the view itself as the last element in the chain. # # @param self this class. # @param request Django-specific request object (of the same # type that is handed to views). # @return Django-specific response object. def __call__(self, request): return self.get_response(request) ## # Main logic for processing the exception. It checks # if the exception captured can be managed, and does it # if so. Otherwise, it lets the native handler operate. # # @param self a @a ExceptionMiddleware object. # @param request Django-specific HTTP request. # @param exception the exception raised from the bank. def process_exception(self, request, exception): # See if we manage this exception. Return None if not. exc_class = None for e in self.excs: if isinstance(exception, e): exc_class = e break if not exc_class: return None # Managed exception. Build response. taler_ec = self.excs.get(exc_class) # The way error codes compose matches # definitions found in [1]. taler_ec += self.apis.get(request.path, 1000) # Check if the endpoint should cause a human-readable # page to be returned. render_to = self.render.get(request.path) if not render_to: return JsonResponse({"ec": taler_ec, "error": exception.hint}, status=exception.http_status_code) request.session["profile_hint"] = \ True, False, exception.hint return redirect(render_to) # [1] https://git.taler.net/exchange.git/tree/src/include/taler_error_codes.h#n1502