summaryrefslogtreecommitdiff
path: root/talerbank/app/views.py
diff options
context:
space:
mode:
authorMarcello Stanisci <stanisci.m@gmail.com>2019-03-07 22:58:09 +0100
committerMarcello Stanisci <stanisci.m@gmail.com>2019-03-07 22:58:09 +0100
commitb050cbfe71367529354c91f2a8a5fe48f52fa317 (patch)
tree74fc88ebcd43be7e64173b6d92613ccde104d644 /talerbank/app/views.py
parenteec9e0e50bd0530b447f5549e33454939b76dc4f (diff)
downloadbank-b050cbfe71367529354c91f2a8a5fe48f52fa317.tar.gz
bank-b050cbfe71367529354c91f2a8a5fe48f52fa317.tar.bz2
bank-b050cbfe71367529354c91f2a8a5fe48f52fa317.zip
Doxygen-commenting views.py.
Diffstat (limited to 'talerbank/app/views.py')
-rw-r--r--talerbank/app/views.py255
1 files changed, 210 insertions, 45 deletions
diff --git a/talerbank/app/views.py b/talerbank/app/views.py
index a9a33ee..150436d 100644
--- a/talerbank/app/views.py
+++ b/talerbank/app/views.py
@@ -116,9 +116,6 @@ def ignore(request):
# @param name name of the session value that should be retrieved.
# @return the value, if found; otherwise False.
def get_session_flag(request, name):
- """
- Get a flag from the session and clear it.
- """
if name in request.session:
ret = request.session[name]
del request.session[name]
@@ -136,9 +133,6 @@ def get_session_flag(request, name):
# @param name hint name
# @return the hint (a "null" one if none was found)
def get_session_hint(request, name):
- """
- Get a hint from the session and clear it.
- """
if name in request.session:
ret = request.session[name]
del request.session[name]
@@ -188,7 +182,12 @@ class InputDatalist(forms.TextInput):
# @param self the object itself
# @param name the name of the value that will be sent
# along the POST
- # @param FIXME.
+ # @param value value to be sent along the @a name.
+ # @param attrs a dict indicating which HTML attribtues should
+ # be defined in the rendered element.
+ # @param renderer render engine (left as None, typically); it
+ # is a class that respects the low-level render API from
+ # Django, see [2]
def render(self, name, value, attrs=None, renderer=None):
html = super().render(
name, value, attrs=attrs, renderer=renderer)
@@ -200,8 +199,11 @@ class InputDatalist(forms.TextInput):
return html + datalist
+##
+# Form for sending wire transfers. It usually appears in the
+# user profile page.
+#
class WTForm(forms.Form):
- '''Form used to wire transfer money internally in the bank.'''
amount = forms.FloatField(
min_value=0.1,
widget=forms.NumberInput(attrs={"class": "currency-input"}))
@@ -210,9 +212,22 @@ class WTForm(forms.Form):
widget=InputDatalist(predefined_accounts_list, "receiver"))
subject = forms.CharField()
-# Check if user's logged in. Check if he/she has withdrawn or
-# registered; render profile page.
+
+##
+# This method serves the profile page, which is the main
+# page where the user interacts with the bank, and also the
+# page that the user gets after a successful login. It has
+# to handle the following cases: (1) the user requests the
+# profile page after haing POSTed a wire transfer request.
+# (2) The user requests the page after having withdrawn coins,
+# that means that a wire transfer has been issued to the exchange.
+# In this latter case, the method has to notify the wallet about
+# the operation outcome. (3) Ordinary GET case, where the
+# straight page should be returned.
+#
+# @param request Django-specific HTTP request object.
+# @return Django-specific HTTP response object.
@login_required
def profile_page(request):
if request.method == "POST":
@@ -258,12 +273,24 @@ def profile_page(request):
return response
+##
+# Helper function that hashes its input. Usually
+# used to hash the response to the math CAPTCHA.
+#
+# @param ans the plain text answer to hash.
+# @return the hashed version of @a ans.
def hash_answer(ans):
hasher = hashlib.new("sha1")
hasher.update(settings.SECRET_KEY.encode("utf-8"))
hasher.update(ans.encode("utf-8"))
return hasher.hexdigest()
+
+##
+# Helper function that makes CAPTCHA's question and
+# answer pair.
+#
+# @return the question and (hashed) answer pair.
def make_question():
num1 = random.randint(1, 10)
operand = random.choice(("*", "+", "-"))
@@ -280,6 +307,16 @@ def make_question():
return question, hash_answer(answer)
+
+
+##
+# This method build the page containing the math CAPTCHA that
+# protects coins withdrawal. It takes all the values from the
+# URL and puts them into the state, for further processing after
+# a successful answer from the user.
+#
+# @param request Django-specific HTTP request object
+# @return Django-specific HTTP response object
@require_GET
@login_required
def pin_tan_question(request):
@@ -310,6 +347,13 @@ def pin_tan_question(request):
return render(request, "pin_tan.html", context)
+
+
+##
+# This method serves the user's answer to the math CAPTCHA,
+# and reacts accordingly to its correctness. If correct (wrong),
+# it redirects the user to the profile page showing a success
+# (failure) message into the informational bar.
@require_POST
@login_required
def pin_tan_verify(request):
@@ -334,15 +378,25 @@ def pin_tan_verify(request):
request.session["just_withdrawn"] = True
return redirect("profile")
+
+
+##
+# Class representing the registration form.
class UserReg(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput())
+
+##
+# This method serves the request for registering a user.
+# If successful, it redirects the user to their profile page;
+# otherwise it will show again the same form (currently, without
+# displaying _any_ error/warning message.)
+#
+# @param request Django-specific HTTP request object.
+# @return Django-specific HTTP response object.
def register(request):
- """
- register a new user giving 100 KUDOS bonus
- """
if request.method != "POST":
return render(request, "register.html")
form = UserReg(request.POST)
@@ -376,15 +430,35 @@ def register(request):
return redirect("profile")
+
+##
+# Logs the user out, redirecting it to the bank's homepage.
+#
+# @param request Django-specific HTTP request object.
+# @return Django-specific HTTP response object.
def logout_view(request):
- """
- Log out the user and redirect to index page.
- """
django.contrib.auth.logout(request)
request.session["login_hint"] = False, True, "Logged out!"
return redirect("index")
+
+##
+# Build the history array.
+#
+# @param account the bank account object whose history is being
+# extracted.
+# @param descending currently not used.
+# @param delta how many history entries will be contained in the
+# array (will be passed as-is to the internal routine
+# @a query_history).
+# @param start any history will be searched starting from this
+# value (which is a row ID), and going to the past or to
+# the future (depending on the user choice). However, this
+# value itself will not be included in the history.
+# @param sign this value ("+"/"-") determines whether the history
+# entries will be younger / older than @a start.
+# @return the history array.
def extract_history(account,
descending,
delta=None,
@@ -413,6 +487,15 @@ def extract_history(account,
return history
+
+##
+# Serve the page showing histories from publicly visible accounts.
+#
+# @param request Django-specific HTTP request object.
+# @param name name of the public account to show.
+# @param page given that public histories are paginated, this
+# value is the page number to display in the response.
+# @return Django-specific HTTP response object.
def serve_public_accounts(request, name=None, page=None):
try:
page = abs(int(page))
@@ -460,7 +543,14 @@ def serve_public_accounts(request, name=None, page=None):
)
return render(request, "public_accounts.html", context)
-
+##
+# Decorator function that authenticates requests by fetching
+# the credentials over the HTTP requests headers.
+#
+# @param view_func function that will be called after the
+# authentication, and that will usually serve the requested
+# endpoint.
+# @return FIXME.
def login_via_headers(view_func):
def _decorator(request, *args, **kwargs):
user_account = auth_and_login(request)
@@ -470,23 +560,35 @@ def login_via_headers(view_func):
return view_func(request, user_account, *args, **kwargs)
return wraps(view_func)(_decorator)
-# Internal function used by /history and /public-accounts. It
-# offers abstraction against the query string definition and DB
-# querying.
-#
-# 'bank_account': whose history is going to be retrieved.
-# 'direction': (both|credit|debit|cancel+|cancel-).
-# 'delta': how many results are going to be extracted. If 'None'
-# is given, no filter of this kind will be applied.
-# 'start': a "id" indicating the first record to be returned.
-# If -1 is given, then the first record will be the youngest
-# and 'delta' records will be returned, _regardless_ of the
-# 'sign' being passed.
-# 'sign': (+|-) indicating that we want records younger|older
-# than 'start'.
-# 'descending': records are sorted older->younger per default,
-# unless this value is true.
+##
+# Helper function that sorts in a descending, or ascending
+# manner, the history elements returned by the internal routine
+# @a query_history_raw.
+#
+# @param bank_account the bank account object whose
+# history is being extracted.
+# @param direction takes the following three values,
+# * debit: only entries where the querying user has _paid_
+# will be returned.
+# * credit: only entries where the querying user _got_
+# paid will be returned.
+# * both: both of the cases above will be returned.
+# * cancel+: only entries where the querying user cancelled
+# the _receiving_ of money will be returned.
+# * cancel-: only entries where the querying user cancelled
+# the _paying_ of money will be returned.
+# @param delta how many history entries will be contained in the
+# array (will be passed as-is to the internal routine
+# @a query_history).
+# @param start any history will be searched starting from this
+# value (which is a row ID), and going to the past or to
+# the future (depending on the user choice). However, this
+# value itself will not be included in the history.
+# @param sign this value ("+"/"-") determines whether the history
+# entries will be younger / older than @a start.
+# @param descending if True, then the results will have the
+# youngest entry in the first position.
def query_history(bank_account,
direction,
delta,
@@ -503,6 +605,29 @@ def query_history(bank_account,
return qs.order_by(order)[:delta]
+
+##
+# Core routine for querying the history of a customer.
+#
+# @param bank_account the bank account object whose
+# history is being extracted.
+# @param direction takes the following three values,
+# * debit: only entries where the querying user has _paid_
+# will be returned.
+# * credit: only entries where the querying user _got_
+# paid will be returned.
+# * both: both of the cases above will be returned.
+# * cancel+: only entries where the querying user cancelled
+# the _receiving_ of money will be returned.
+# * cancel-: only entries where the querying user cancelled
+# the _paying_ of money will be returned.
+# @param start any history will be searched starting from this
+# value (which is a row ID), and going to the past or to
+# the future (depending on the user choice). However, this
+# value itself will not be included in the history.
+# @param sign this value ("+"/"-") determines whether the history
+# entries will be younger / older than @a start.
+# @return the query set (that will be typically further processed).
def query_history_raw(bank_account, direction, start, sign):
direction_switch = {
"both": (Q(debit_account=bank_account) |
@@ -516,7 +641,7 @@ def query_history_raw(bank_account, direction, start, sign):
}
sign_filter = {
- "+": Q(id__gt=start),
+ "+": Q(id__gt=start), # default
"-": Q(id__lt=start),
}
@@ -525,13 +650,15 @@ def query_history_raw(bank_account, direction, start, sign):
sign_filter.get(sign));
+##
+# Serve a request of /history.
+#
+# @param request Django-specific HTTP request.
+# @param user_account the account whose history should be gotten.
+# @return Django-specific HTTP response object.
@require_GET
@login_via_headers
def serve_history(request, user_account):
- """
- This API is used to get a list of transactions related to one
- user.
- """
validate_data(request, request.GET.dict())
# delta
parsed_delta = re.search(r"([\+-])?([0-9]+)",
@@ -572,6 +699,14 @@ def serve_history(request, user_account):
return HttpResponse(status=204)
return JsonResponse(dict(data=history), status=200)
+
+##
+# Helper function that authenticates a user by fetching the
+# credentials from the HTTP headers. Typically called from
+# decorators.
+#
+# @param request Django-specific HTTP request object.
+# @return Django-specific "authentication object".
def auth_and_login(request):
"""Return user instance after checking authentication
credentials, False if errors occur"""
@@ -595,6 +730,16 @@ def auth_and_login(request):
username=username,
password=password)
+
+
+##
+# Serve a request of /reject (for rejecting wire transfers).
+#
+# @param request Django-specific HTTP request object.
+# @param user_account the account that is going to reject the
+# transfer. Used to check whether they have this right
+# or not (only accounts which _got_ payed can cancel the
+# transaction.)
@transaction.atomic
@csrf_exempt
@require_http_methods(["PUT", "POST"])
@@ -613,17 +758,19 @@ def reject(request, user_account):
return HttpResponse(status=204)
+
+##
+# Serve a request to make a wire transfer. Allows fintech
+# providers to issues payments in a programmatic way.
+#
+# @param request Django-specific HTTP request object.
+# @param user_account the (authenticated) user issuing this
+# request.
+# @return Django-specific HTTP response object.
@csrf_exempt
@require_POST
@login_via_headers
def add_incoming(request, user_account):
- """
- Internal API used by exchanges to notify the bank
- of incoming payments.
-
- This view is CSRF exempt, since it is not used from
- within the browser, and only over the private admin interface.
- """
data = json.loads(request.body.decode("utf-8"))
validate_data(request, data)
subject = "%s %s" % (data["subject"], data["exchange_url"])
@@ -638,6 +785,15 @@ def add_incoming(request, user_account):
"timestamp": "/Date(%s)/" % int(wtrans.date.timestamp())})
+
+
+##
+# Serve a Taler withdrawal request; takes the amount chosen
+# by the user, and builds a response to trigger the wallet into
+# the withdrawal protocol
+#
+# @param request Django-specific HTTP request.
+# @return Django-specific HTTP response object.
@login_required
@require_POST
def withdraw_nojs(request):
@@ -660,6 +816,14 @@ def withdraw_nojs(request):
settings.TALER_SUGGESTED_EXCHANGE
return response
+
+##
+# Make a wire transfer between two accounts (internal to the bank)
+#
+# @param amount how much money the wire transfer is worth.
+# @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):
LOGGER.debug("%s => %s, %s, %s" %
@@ -722,3 +886,4 @@ def wire_transfer(amount, debit_account, credit_account,
return transaction_item
# [1] https://stackoverflow.com/questions/24783275/django-form-with-choices-but-also-with-freetext-option
+# [2] https://docs.djangoproject.com/en/2.1/ref/forms/renderers/#low-level-widget-render-api