diff options
author | Florian Dold <florian.dold@gmail.com> | 2016-04-08 16:52:20 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2016-04-08 17:00:43 +0200 |
commit | 90072481161b3361da0af9a13b51a468764c1987 (patch) | |
tree | cbaf72af02b9b1d23a042217d3b1aa677b1c09c4 /talerbank | |
parent | 8bf550786257a83d76f6a2864484a47fb4d1b9e9 (diff) | |
download | bank-90072481161b3361da0af9a13b51a468764c1987.tar.gz bank-90072481161b3361da0af9a13b51a468764c1987.tar.bz2 bank-90072481161b3361da0af9a13b51a468764c1987.zip |
restructure modules
Diffstat (limited to 'talerbank')
46 files changed, 2893 insertions, 0 deletions
diff --git a/talerbank/__init__.py b/talerbank/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/talerbank/__init__.py diff --git a/talerbank/app/__init__.py b/talerbank/app/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/talerbank/app/__init__.py diff --git a/talerbank/app/admin.py b/talerbank/app/admin.py new file mode 100644 index 0000000..e5f6359 --- /dev/null +++ b/talerbank/app/admin.py @@ -0,0 +1,11 @@ +""" +this file is in the public domain +""" +from django.contrib import admin +from .models import (BankAccount, + History) + +admin.site.register(BankAccount) +admin.site.register(History) + +# Register your models here. diff --git a/talerbank/app/apps.py b/talerbank/app/apps.py new file mode 100644 index 0000000..1912612 --- /dev/null +++ b/talerbank/app/apps.py @@ -0,0 +1,10 @@ +""" +this file is in the public domain +""" +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class BankConfig(AppConfig): + name = 'Bank' diff --git a/talerbank/app/captcha.py b/talerbank/app/captcha.py new file mode 100644 index 0000000..e5909b2 --- /dev/null +++ b/talerbank/app/captcha.py @@ -0,0 +1,162 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from .user_mgmt import is_logged +from .funds_mgmt import (Reserve, + create_reserve_at_exchange) +from .schemas import Schemas +from .lib import (write_log_file, + get_currency, + check_db, + get_pretty_exchange_details, + floatify_amount) +from simplemathcaptcha.fields import (MathCaptchaField, + MathCaptchaWidget) +from django.http import HttpResponse, JsonResponse +from django.shortcuts import render +from django.conf import settings +from django import forms +from urllib.parse import (urlunparse, + urlparse, + unquote) +import hashlib +import json +from .errors import (UserNotLogged, + BadAmount, + MissingGetParameter, + WrongMethod, + user_not_logged_handler, + BadGetParameter, + BadPostValue, + BadWireDetails, + ExchangeUnknown, + NoBankMatch, + NoWireMethodMatch, + bad_get_parameter_handler, + bad_post_value_handler, + exchange_unknown_handler, + wrong_method_handler) + + +class Pin(forms.Form): + pin = MathCaptchaField( + widget=MathCaptchaWidget( + question_tmpl="<div lang=\"en\">What is %(num1)i %(operator)s %(num2)i ?</div>")) + + +@is_logged +def pin_tan_question_attempt(request): + if request.method != 'GET': + raise WrongMethod('GET') + for param in ["amount_value", + "amount_fraction", + "amount_currency", + "exchange", + "reserve_pub", + "wire_details"]: + if param not in request.GET: + raise MissingGetParameter(param) + try: + amount = {'value': int(request.GET['amount_value']), + 'fraction': int(request.GET['amount_fraction']), + 'currency': request.GET['amount_currency']} + except ValueError: + raise BadGetParameter + wiredetails = json.loads(unquote(request.GET['wire_details'])) + Schemas.validate_wiredetails(wiredetails, Schemas.wiredetails_schema) + if "test" not in wiredetails: + raise NoWireMethodMatch + # always false to run local tests.. + if urlparse(wiredetails['test']['bank_uri']).netloc != request.META['HTTP_HOST'] and False: + raise NoBankMatch + request.session['account_number'] = wiredetails['test']['account_number'] + Schemas.validate_amount(amount, Schemas.amount_schema) # raise BadAmount + request.session['amount'] = amount + request.session['exchange'] = request.GET['exchange'] + request.session['reserve_pub'] = request.GET['reserve_pub'] + return render(request, 'pin_tan.html', {'form': Pin(auto_id=False), + 'amount': floatify_amount(amount), + 'currency': get_currency(request), + 'exchange': + get_pretty_exchange_details(request.GET['exchange'])}) + + +@is_logged +def pin_tan_question(request): + try: + return pin_tan_question_attempt(request) + except MissingGetParameter as e: + return JsonResponse({'reason': e.par + " missing in query parameters"}, status=400) + except (BadAmount, BadGetParameter): + return JsonResponse({'reason': "bad amount given"}, status=400) + except WrongMethod as e: + return JsonResponse({'reason': 'only GET method allowed'}, status=405) + except BadWireDetails: + return JsonResponse({'reason': "bad wiredetails given"}, status=400) + except ValueError: + return JsonResponse({'reason': "garbage in query string"}, status=400) + except NoWireMethodMatch: + return JsonResponse({'reason': "incompatible wire methods"}, status=400) + except NoBankMatch: + return JsonResponse({'reason': "bank given in wiredetails is not this one"}, status=400) + + +@check_db +@is_logged +def pin_tan_verify(request): + try: + return pin_tan_verify_attempt(request) + except WrongMethod as e: + return wrong_method_handler(request, e) + except UserNotLogged: + return user_not_logged_handler(request) + except BadPostValue: + return bad_post_value_handler(request) + + +def pin_tan_verify_attempt(request): + if request.method != 'POST': + raise WrongMethod("POST") + try: + given = request.POST['pin_0'] + hashed_result = request.POST['pin_1'] + except Exception: # FIXME narrow the Exception type + raise BadPostValue + hasher = hashlib.new("sha1") + hasher.update(settings.SECRET_KEY.encode('utf-8')) + hasher.update(given.encode('utf-8')) + hashed_attempt = hasher.hexdigest() + if hashed_attempt == hashed_result: + for param in ["amount", "exchange", "reserve_pub"]: + if param not in request.session: + return HttpResponse("Not a withdraw session", status=400) + reserve = Reserve(request.session['amount'], + request.session['exchange'], + request.session['account_number'], + request.session['reserve_pub'], + 'TEST') + success_url = urlunparse([request.scheme, + request.META['HTTP_HOST'], + "/success.html", '', '', '']) + try: + return create_reserve_at_exchange(request, success_url, reserve) + except ExchangeUnknown: + return exchange_unknown_handler(request) + + else: + return render(request, 'error.html', {'type': "wrong_pin"}, status=400) diff --git a/talerbank/app/config.py b/talerbank/app/config.py new file mode 100644 index 0000000..3cf223f --- /dev/null +++ b/talerbank/app/config.py @@ -0,0 +1,6 @@ +""" +this file is in the public domain +""" +pre_accounts = ['Tor', 'GNUnet', 'Taler', 'FSF'] +expects_donations = ['Tor', 'GNUnet', 'Taler', 'FSF'] +explicit_currency = 'PUDOS' diff --git a/talerbank/app/config.py.in b/talerbank/app/config.py.in new file mode 100644 index 0000000..9e61b3b --- /dev/null +++ b/talerbank/app/config.py.in @@ -0,0 +1,6 @@ +""" +this file is in the public domain +""" +pre_accounts = ['Tor', 'GNUnet', 'Taler', 'FSF'] +expects_donations = ['Tor', 'GNUnet', 'Taler', 'FSF'] +explicit_currency = '@currencyfinal@' diff --git a/talerbank/app/errors.py b/talerbank/app/errors.py new file mode 100644 index 0000000..09159ca --- /dev/null +++ b/talerbank/app/errors.py @@ -0,0 +1,135 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from django.shortcuts import render + + +class BadAmount(Exception): + pass + + +class BadWireDetails(Exception): + pass + + +class BadIncomingRequest(Exception): + pass + + +class MissingGetParameter(Exception): + def __init__(self, par_name): + self.par = par_name + + +class BadGetParameter(Exception): + pass + + +class NoWireMethodMatch(Exception): + pass + + +class BadPostValue(Exception): + pass + + +class WrongMethod(Exception): + def __init__(self, allowed_method): + self.allowed_method = allowed_method + + +class ExchangeUnknown(Exception): + pass + + +class UserNotLogged(Exception): + pass + + +class SubPathNotFound(Exception): + pass + + +class CurrencyMismatch(Exception): + pass + + +class NoTalerDatabase(Exception): + pass + + +class NoBankAccount(Exception): + pass + + +class NoBankMatch(Exception): + pass + + +def no_bank_account_handler(request): + return internal_error_handler( + request, + "(The bank itself has no account," + " please run 'taler-bank-manage --preaccounts')") + + +def non_supported_wire_method(request): + return render(request, 'error.html', {'type': 'non_supported_method'}, status=400) + + +def non_existent_db_handler(request): + return internal_error_handler(request, "(db does not exist)") + + +def internal_error_handler(request, hint=False): + return render(request, 'error.html', {'type': 'internal_error', 'hint': hint}, status=500) + + +def user_not_logged_handler(request): + return render(request, 'error.html', {'type': 'not_logged'}, status=401) + + +def exchange_unknown_handler(request): + return render(request, 'error.html', {'type': 'exchange_unknown'}, status=401) + + +def wrong_method_handler(request, err): + return render(request, + 'error.html', + {'type': 'custom', + 'custom_title': "Wrong method", + 'custom_message': "Only " + err.allowed_method + " allowed"}, + status=405) + + +def bad_get_parameter_handler(request): + return render(request, + 'error.html', + {'type': 'custom', + 'custom_title': "Bad request", + 'custom_message': "Invalid parameter in GET request"}, + status=405) + + +def bad_post_value_handler(request): + return render(request, + 'error.html', + {'type': 'custom', + 'custom_title': "Bad request", + 'custom_message': "Bad value in POSTed data"}, + status=400) diff --git a/talerbank/app/funds_mgmt.py b/talerbank/app/funds_mgmt.py new file mode 100644 index 0000000..2b45bc0 --- /dev/null +++ b/talerbank/app/funds_mgmt.py @@ -0,0 +1,148 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from .user_mgmt import is_logged +from .schemas import Schemas +from .errors import (BadIncomingRequest, + CurrencyMismatch, + WrongMethod) +from .lib import (write_log_file, + check_exchange_account_no, + wire_transfer_in_out) +from django.views.decorators.csrf import csrf_exempt +from django.http import (HttpResponse, + JsonResponse) +from .models import BankAccount +from django.shortcuts import (render, + redirect) +from django.db.utils import OperationalError +from django import forms +from django.http import HttpResponseServerError +from urllib.parse import (urlparse, + urljoin) +import requests +import time +import json + + +class DebugForm(forms.Form): + reserve_pk = forms.CharField(initial="DVHS4CQRC3ZQPW9PD6A5BCTYS9Z460P261M6M660QS7N16BXP19G") + kudos_amount = forms.CharField(initial="3.3 KUDOS") + mint_rcv = forms.CharField(initial="http://mint.demo.taler.net/") + + +class Reserve: + def __init__(self, amount, exchange, exchange_account, reserve_pub, wire_type): + # it raises BadAmount if amount is bad + Schemas.validate_amount(amount, Schemas.amount_schema) + self.amount = amount + self.exchange = exchange + self.exchange_account = exchange_account + self.reserve_pub = reserve_pub + self.wire_type = wire_type + + +class Transaction: + def __init__(self, direction, amount, counterpart="Unknown"): + self.direction = direction + self.amount = amount + self.counterpart = counterpart + +""" +The CSRF exempt is due to the fact Django looks for an anti-CSRF token +in any POST it gets. Since the following function is meant to serve mints, +and mints don't send any such token, it is necessary to disable it. +Those tokens are normally hidden fields in Django-generated HTML forms. +""" + + +@csrf_exempt +def add_incoming_attempt(request): + try: + return add_incoming(request) + except ValueError: + return JsonResponse({'reason': 'malformed json'}, status=400) + except WrongMethod: + return HttpResponse("Only POST allowed, and not from browsers", status=405) + except BadIncomingRequest: + return JsonResponse({'reason': 'malformed json, validation failed'}, status=400) + except TypeError: + return JsonResponse({'reason': 'mispelled fieldname in JSON or unknown data sent..'}, status=400) + except BankAccount.DoesNotExist: + return JsonResponse({'reason': 'credit or debit account does not exist'}, status=404) + except BankAccount.MultipleObjectsReturned: + return JsonResponse({'reason': 'internal error (collision in db)'}, status=500) + except OperationalError: + return JsonResponse({'reason': 'internal error (bank has no db)'}, status=500) + except CurrencyMismatch: + return JsonResponse({'reason': 'currency mismatch between credit/debit account and amount'}, + status=500) + + +@csrf_exempt +def add_incoming(request): + if request.method != 'POST': + raise WrongMethod('GET') + data = json.loads(request.body.decode('utf-8')) + Schemas.validate_incoming_request(data, Schemas.incoming_request_schema) + wire_transfer_in_out(data['amount'], + data['debit_account'], + data['credit_account'], + data['wtid']) + return JsonResponse({'outcome': 'ok'}, status=200) + + +@is_logged +def withdraw_attempt(request): + return render(request, 'withdraw.html', {'account_no': request.session["account_no"]}) + + +@is_logged +def withdraw_process(request): + return withdraw_attempt(request) + + +def create_reserve_at_exchange(request, success_url, reserve_set): + if not isinstance(reserve_set, Reserve): + return HttpResponseServerError + o = urlparse(reserve_set.exchange) + if o.scheme == '' or o.netloc == '' or o.path != '/': + return JsonResponse({'error': 'bad exchange base url', + 'given_url': reserve_set.mint}, status=400) + amount = {'value': reserve_set.amount['value'], + 'fraction': reserve_set.amount['fraction'], + 'currency': reserve_set.amount['currency']} + # Should raise ExchangeUnknown .. + check_exchange_account_no(reserve_set.exchange_account) + json_body = {'reserve_pub': reserve_set.reserve_pub, + 'execution_date': "/Date(" + str(int(time.time())) + ")/", + 'wire': {'type': reserve_set.wire_type, + 'account_number': reserve_set.exchange_account}, + 'amount': amount} + res = requests.post(urljoin(reserve_set.exchange, '/admin/add/incoming'), json=json_body) + if res.status_code != 200: + return JsonResponse({'error': "internal error", + 'hint': "wire transfer failed (bad response)", + 'status': res.status_code, + 'body': res.text}, status=500) + wire_transfer_in_out(amount, + request.session['account_no'], + reserve_set.exchange_account, + "withdrawal") + request.session['withdrawal_successful'] = True + return redirect('/profile') diff --git a/talerbank/app/history.py b/talerbank/app/history.py new file mode 100644 index 0000000..bfe80be --- /dev/null +++ b/talerbank/app/history.py @@ -0,0 +1,79 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from .lib import (get_logged_user_account, + get_public_accounts, + get_bank_account_from_username, + write_log_file, + check_db, + is_logged, + get) +from .errors import internal_error_handler +from django.shortcuts import (render, + redirect) + + +def extract_history(bank_account): + ret = [] + for item in bank_account.history_set.all(): + ret.append({'float_amount': item.amount, + 'float_currency': item.currency, + 'direction': 'FROM' if item.direction == 'IN' else 'TO', + 'counterpart': item.counterpart, + 'subject': item.subject, + 'date': item.date.strftime("%d/%m/%y")}) + return ret + + +def history_attempt(request): + user_bank_account = get_logged_user_account(request.session['account_no']) + render_history = [] + render_history = extract_history(user_bank_account) + return render(request, 'history.html', {'history': render_history}) + + +@check_db +@is_logged +def history_process(request): + return history_attempt(request) + + +@check_db +def public_accounts_process(request): + return public_accounts_attempt(request) + + +def public_accounts_attempt(request): + accounts = [] + for item in get_public_accounts(): + accounts.append({'account_name': item.user.username}) + sel_account_name = get(request.GET, 'account') + if not sel_account_name: + return redirect("/public-accounts/?account=Tor") + sel_account = get_bank_account_from_username(sel_account_name) + if not sel_account: + return internal_error_handler(request) + history = extract_history(sel_account) + return render(request, + 'public_histories_reloaded.html', + {'public_accounts': accounts, + 'selected_account': + {'account_name': sel_account_name, + 'account_no': sel_account.account_no, + 'history': history} + }) diff --git a/talerbank/app/lib.py b/talerbank/app/lib.py new file mode 100644 index 0000000..bca4ac7 --- /dev/null +++ b/talerbank/app/lib.py @@ -0,0 +1,208 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from .models import BankAccount, History +from .errors import (SubPathNotFound, + user_not_logged_handler, + ExchangeUnknown, + CurrencyMismatch, + internal_error_handler, + no_bank_account_handler, + non_existent_db_handler) +from .config import explicit_currency +from urllib.parse import urlparse +from os.path import split +from django.contrib.auth.models import User +from django.db.utils import (OperationalError, + ProgrammingError) + + +def get(arr, key, default=False): + if key not in arr: + return default + return arr[key] + + +def get_pin_tan_url(request): + return request.build_absolute_uri("/auth/pin/question") + + +# return the path's part after 'limit' +# example: given /a/b/c and limit='b', returns 'c' +def get_relative_parameter(request, limit): + path = request.path + l = [''] + while True: + p = split(path) + if p[1] == limit: + break + if p[0] == '/' and p[1] == '': + raise SubPathNotFound + l.append(p[1]) + path = p[0] + return l.pop() + + +def get_currency(request): + switcher = {'bank.demo.taler.net': 'KUDOS', + 'bank.test.taler.net': 'PUDOS'} + if not request: + return explicit_currency + return switcher.get(request.META['HTTP_HOST'], explicit_currency) + + +def get_public_accounts(): + try: + return BankAccount.objects.filter(is_public=True) + except BankAccount.DoesNotExist: + return [] + + +def check_exchange_account_no(account_no): + try: + BankAccount.objects.get(account_no=account_no) + except BankAccount.DoesNotExist: + raise ExchangeUnknown + + +def get_exchange_account_no(exchange_baseurl): + normalized_url = normalize_exchange_url(exchange_baseurl) + exchange = get_bank_account_from_username(normalized_url) + if exchange: + return exchange.account_no + raise ExchangeUnknown + + +# if the exchange has an account here, it returns its 'username' +# (= its base url) and account number, otherwise it just replies +# its base url +def get_pretty_exchange_details(exchange_baseurl): + normalized_url = normalize_exchange_url(exchange_baseurl) + exchange = get_bank_account_from_username(normalized_url) + if exchange: + return normalized_url + " (account #" + str(exchange.account_no) + ")" + return normalized_url + + +def normalize_exchange_url(exchange_baseurl): + exchange_username = urlparse(exchange_baseurl) + normalized_url = exchange_username.scheme + '://' + exchange_username.netloc + return normalized_url + + +def get_bank_account_from_username(username): + try: + user_account = User.objects.get(username=username) + return user_account.bankaccount + except User.DoesNotExist: + return False + + +def get_logged_user_account(number): + return BankAccount.objects.get(account_no=number) + + +def floatify_amount(amount_dict): + return amount_dict['value'] + (amount_dict['fraction'] / 1000000) + + +def write_log_file(data, log_file="/tmp/django.log", flag='a'): + f = open(log_file, flag) + f.write(data) + f.close() + + +def check_db(fn): + def fake_query(request): + try: + User.objects.get(username='Bank') + except User.DoesNotExist: + return no_bank_account_handler(request) + except ProgrammingError: + return internal_error_handler(request, + "(db available but no tables in it. Run" + " 'taler-bank-manage --definetables')") + except OperationalError: + return non_existent_db_handler(request) + return fn(request) + return fake_query + + +def is_logged(fn): + def check_login(request): + if "account_no" not in request.session: + return user_not_logged_handler(request) + return fn(request) + return check_login + + +# FIXME to be obsoleted by 'wire_tranfer()' +def transfer_in(amount, account_no, wtid, counterpart="unknown"): + account = BankAccount.objects.get(account_no=account_no) + float_amount = floatify_amount(amount) + account.balance += float_amount + account.save() + history_item = History(amount=float_amount, + currency=amount['currency'], + direction="IN", + counterpart=counterpart, + subject=wtid, + account=account) + history_item.save() + + +def wire_transfer_in_out(amount, + debit, + credit, + wtid): + debit_account = BankAccount.objects.get(account_no=debit) + credit_account = BankAccount.objects.get(account_no=credit) + wire_transfer(amount, + debit, + "OUT", + wtid, + credit_account.user.username + " (account #" + str(credit) + ")") + wire_transfer(amount, + credit, + "IN", + wtid, + debit_account.user.username + " (account #" + str(debit) + ")") + + +# transfer funds from/to 'account_no' (used as a subroutine) +def wire_transfer(amount, + account_no, + direction, + wtid="not given", + counterpart="unknown"): + account = BankAccount.objects.get(account_no=account_no) + if account.currency != amount['currency']: + raise CurrencyMismatch + float_amount = floatify_amount(amount) + if "IN" == direction: + account.balance += float_amount + else: + account.balance -= float_amount + account.save() + history_item = History(amount=float_amount, + currency=amount['currency'], + direction=direction, + counterpart=counterpart, + subject=wtid, + account=account) + history_item.save() diff --git a/talerbank/app/management/__init__.py b/talerbank/app/management/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/talerbank/app/management/__init__.py diff --git a/talerbank/app/management/commands/__init__.py b/talerbank/app/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/talerbank/app/management/commands/__init__.py diff --git a/talerbank/app/management/commands/bank_admin.py b/talerbank/app/management/commands/bank_admin.py new file mode 100644 index 0000000..0bad3a5 --- /dev/null +++ b/talerbank/app/management/commands/bank_admin.py @@ -0,0 +1,59 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2106 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from random import randint +from django.core.management.base import BaseCommand +from django.contrib.auth.models import User +import argparse + +def main(options): + if options['op'] != 'users': + print("Operation not supported") + return + if 'reset_password' not in options: + print("Only --reset-password allowed") + return + try: + username = options['reset_password'].pop(0) + new_password = options['reset_password'].pop(0) + user = User.objects.get(username=username) + user.set_password(new_password) + user.save() + + except User.DoesNotExist: + print("Non existent user, exiting..") + return + + except OperationalError: + print("Non existent DB, exiting..") + return + +class Command(BaseCommand): + help = "Tool for doing various administration tasks" + + def add_arguments(self, parser): + parser.add_argument('op', + help="Switch on the type of operation to accomplish." + " Supported switches are: 'users'") + parser.add_argument('--reset-password', + nargs=2, + metavar=('USER', 'NEWPASS'), + help="Change USER's password to NEWPASS") + + def handle(self, *args, **options): + main(options) diff --git a/talerbank/app/management/commands/basic_accounts.py b/talerbank/app/management/commands/basic_accounts.py new file mode 100644 index 0000000..78435ba --- /dev/null +++ b/talerbank/app/management/commands/basic_accounts.py @@ -0,0 +1,26 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2106 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" +from django.core.management.base import BaseCommand +from django.contrib.auth.models import User +from django.db.utils import OperationalError +from ...startup import basic_accounts + + +class Command(BaseCommand): + def handle(self, *args, **options): + basic_accounts() diff --git a/talerbank/app/management/commands/check_db.py b/talerbank/app/management/commands/check_db.py new file mode 100644 index 0000000..b8c50f9 --- /dev/null +++ b/talerbank/app/management/commands/check_db.py @@ -0,0 +1,46 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2106 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" +from django.core.management.base import BaseCommand +from django.contrib.auth.models import User +from django.db.utils import OperationalError + + +def check_db(): + try: + bank = User.objects.get(username='Bank') + print ("The DB contains the bank's bank account, so it is assumed to" + " work properly") + except User.DoesNotExist: + print("\n*****WARNING*****\nCreate predefined accounts" + " before running the bank.\n" + "Try 'taler-bank-manage --preaccounts\n\n" + "**********\n") + return 1 + except OperationalError: + print("\n*****WARNING*****\n\nPlease create 'talertest'" + " database before running the bank.\n" + "Create also predefined accounts (with" + " 'taler-bank-manage --preaccounts')\n\n" + "**********\n") + return 1 + return 0 + + +class Command(BaseCommand): + def handle(self, *args, **options): + check_db() diff --git a/talerbank/app/management/commands/dump_talerdb.py b/talerbank/app/management/commands/dump_talerdb.py new file mode 100644 index 0000000..f245eed --- /dev/null +++ b/talerbank/app/management/commands/dump_talerdb.py @@ -0,0 +1,57 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2106 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" +from django.core.management.base import BaseCommand +from ...models import (BankAccount, + History) +from django.db.utils import OperationalError +from time import gmtime, strftime + +error_message = "\n*****ERROR*****\n\nPlease create" \ + + " the database before running this test." \ + + "**********\n" + + +def dump_accounts(): + try: + accounts = BankAccount.objects.all() + if accounts.count() < 1: + print("No accounts created yet..") + return + for acc in accounts: + print(acc.user.username + " has account number " + str(acc.account_no)) + except OperationalError: + print(error_message) + +def dump_history(): + try: + history = History.objects.all() + for item in history: + print("Account " + item.account.user.username + ": " + + item.direction + " " + str(item.amount) + " " + + item.currency + " on " + str(item.date)) + + + + except OperationalError: + print(error_message) + + +class Command(BaseCommand): + def handle(self, *args, **options): + dump_accounts() + dump_history() diff --git a/talerbank/app/management/commands/pre_accounts.py b/talerbank/app/management/commands/pre_accounts.py new file mode 100644 index 0000000..35e8bd1 --- /dev/null +++ b/talerbank/app/management/commands/pre_accounts.py @@ -0,0 +1,53 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2106 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" +from django.core.management.base import BaseCommand +from django.db.utils import DataError +from django.contrib.auth.models import User +from django.db.utils import OperationalError +from ...models import BankAccount +from ...lib import get_currency +from ...lib import write_log_file +from ...config import pre_accounts + + +def make_accounts(): + for u in pre_accounts: + print("Processing " + u) + try: + User.objects.get(username=u) + except OperationalError: + print("Please create DB before running this option") + return + except User.DoesNotExist: + try: + a = User.objects.create_user(username=u, password='') + except DataError: + print("Given username exceeded 30 chars, please make it shorter!") + return + is_public = not (u == 'http://mint.test.taler.net' or + u == 'http://mint.demo.taler.net') + b = BankAccount(user=a, + currency=get_currency(None), + is_public=is_public) + b.save() + print (" getting account number " + str(b.account_no)) + + +class Command(BaseCommand): + def handle(self, *args, **options): + make_accounts() diff --git a/talerbank/app/management/commands/sample_donations.py b/talerbank/app/management/commands/sample_donations.py new file mode 100644 index 0000000..06e4599 --- /dev/null +++ b/talerbank/app/management/commands/sample_donations.py @@ -0,0 +1,62 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2106 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from random import randint +from django.core.management.base import BaseCommand +from ...lib import (write_log_file, + get_public_accounts, + get_currency, + wire_transfer_in_out) +from ...config import expects_donations +from ...models import BankAccount + + +def sample_donations(): + public_accounts = get_public_accounts() + for account in public_accounts: + for i in range(0, 9): + if account.user.username in expects_donations: + value = randint(1, 100) + try: + # 1st take money from bank and give to exchange + wire_transfer_in_out({'value': value, + 'fraction': 0, + 'currency': get_currency(None)}, + 1, + 2, + "Test withdrawal") + + # 2nd take money from exchange and give to donation receiver + wire_transfer_in_out({'value': value, + 'fraction': 0, + 'currency': get_currency(None)}, + 2, + account.account_no, + "Test donation(s)") + except BankAccount.DoesNotExist: + print("Please create basic accounts before. Run taler-bank-manage --preaccounts") + return + except OperationalError: + print("No DB found, please create one beforehand. Try with taler-bank-manage --createdb") + return + + + +class Command(BaseCommand): + def handle(self, *args, **options): + sample_donations() diff --git a/talerbank/app/migrations/0001_initial.py b/talerbank/app/migrations/0001_initial.py new file mode 100644 index 0000000..d92797c --- /dev/null +++ b/talerbank/app/migrations/0001_initial.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-04-08 14:17 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='BankAccount', + fields=[ + ('is_public', models.BooleanField(default=False)), + ('balance', models.FloatField(default=0)), + ('currency', models.CharField(default='', max_length=12)), + ('account_no', models.AutoField(primary_key=True, serialize=False)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='History', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', models.FloatField(default=0)), + ('currency', models.CharField(max_length=12)), + ('direction', models.CharField(max_length=4)), + ('counterpart', models.CharField(default='unknown', max_length=200)), + ('subject', models.CharField(default='not given', max_length=200)), + ('date', models.DateField(auto_now=True)), + ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='app.BankAccount')), + ], + ), + ] diff --git a/talerbank/app/migrations/__init__.py b/talerbank/app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/talerbank/app/migrations/__init__.py diff --git a/talerbank/app/models.py b/talerbank/app/models.py new file mode 100644 index 0000000..45bffdb --- /dev/null +++ b/talerbank/app/models.py @@ -0,0 +1,52 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from __future__ import unicode_literals +from django.contrib.auth.models import User +from django.db import models + + +class BankAccount(models.Model): + is_public = models.BooleanField(default=False) + balance = models.FloatField(default=0) + currency = models.CharField(max_length=12, default="") + # this value (below) needs not to be unique in some cases: for + # example when an http and https exchanges want to have the same + # bank account number + account_no = models.AutoField(primary_key=True) + user = models.OneToOneField(User, + on_delete=models.CASCADE) + + +class History(models.Model): + amount = models.FloatField(default=0) + currency = models.CharField(max_length=12) + direction = models.CharField(max_length=4) + counterpart = models.CharField(default="unknown", max_length=200) + subject = models.CharField(default="not given", max_length=200) + date = models.DateField(auto_now=True) + account = models.ForeignKey(BankAccount, on_delete=models.CASCADE) + + def set_balance(self, new_balance): + if isinstance(new_balance, int) or isinstance(new_balance, float): + self.balance = new_balance + else: + print("Not a number given for new balance") + + def push_history(self, obj): + self.history.append(obj) diff --git a/talerbank/app/my-static/favicon.ico b/talerbank/app/my-static/favicon.ico new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/talerbank/app/my-static/favicon.ico diff --git a/talerbank/app/my-static/style.css b/talerbank/app/my-static/style.css new file mode 100644 index 0000000..923654b --- /dev/null +++ b/talerbank/app/my-static/style.css @@ -0,0 +1,158 @@ +/* + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci + @author Gabor Toth +*/ + +body { + background-color: white; + margin: 0; + padding: 0; + font-family: Verdana, sans; +} + +header { + width: 100%; + height: 100px; + margin: 0; + padding: 0; + border-bottom: 1px solid black; +} + +header h1 { + font-size: 200%; + margin: 15 0 0 120px; +/* position: relative; + top: 50%; + transform: translateY(-50%);*/ +} +header #logo { + float: left; + width: 100px; + height: 100px; + padding: 0; + margin: 0; + text-align: center; + border-right: 1px solid black; +} + +section#menu { + margin: 0 0 90 0; + padding: 5px; + border-right: 1px solid black; + height: 100%; + width: 90px; + float: left; +} + +section#main { + margin: 0 0 0 100px; + padding: 20px; + border-left: 1px solid black; + height: 100%; + max-width: 40em; +} + +section#main h1:first-child { + margin-top: 0; +} + +div.login-form, div.register-form { + border-radius: 10px; + background-color: #f2f2f2; + padding: 11px 27px 44px 27px; + max-width: 200px; +} + +div.login-form input[type=submit], +div.register-form input[type=submit] { + width-max: 30%; + float: right; + padding: 6px; +} + +.selected-item { + border-style: solid; + border-width: 1px; +} + +.informational { + border-radius: 8px; + padding: 8px; +} + +.informational-ok { + background: #ccffcc; +} + +.informational-fail { + background: #ff8566; +} + +div.login-form input[type=text], +div.login-form input[type=password], +div.register-form input[type=text], +div.register-form input[type=password] { + width: 100%; + padding: 12px 20px; + margin: 8px 0; + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; +} + +h1 { + font-size: 160%; +} + +h2 { + font-size: 140%; +} + +h3 { + font-size: 120%; +} + +h4, h5, h6 { + font-size: 100%; +} + +table.history { + margin: 30px 0px; + border-width: 0px; + border-spacing: 3px; + border-style: groove; + border-color: gray; + border-collapse: separate; + background-color: white; +} +table.history th { + border-width: 1px; + padding: 5px; + border-style: outset; + border-color: gray; + background-color: white; + -moz-border-radius: ; +} +table.history td { + border-width: 1px; + padding: 5px; + border-style: outset; + border-color: gray; + background-color: white; + -moz-border-radius: ; +} diff --git a/talerbank/app/schemas.py b/talerbank/app/schemas.py new file mode 100644 index 0000000..abec0d3 --- /dev/null +++ b/talerbank/app/schemas.py @@ -0,0 +1,73 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +import validictory +from .errors import (BadIncomingRequest, + BadWireDetails, + BadAmount) + +# definitions of JSON schemas for validating data + + +class Schemas: + wiredetails_schema = {"type": "object", + "properties": { + "test": {"type": "object", + "properties" : { + "type" : {"type": "string"}, + "account_number": {"type": "integer"}, + "bank_uri": {"type": "string"}}}}} + + + amount_schema = {"type": "object", + "properties": { + "value": {"type": "integer"}, + "fraction": {"type": "integer"}, + "currency": {"type": "string"}}} + + incoming_request_schema = {"type": "object", + "properties": { + "amount": {"type": amount_schema}, + "wtid": {"type": "string"}, + "credit_account": {"type": "integer"}, + "debit_account": {"type": "integer"}}} + + @staticmethod + def validate_amount(amount, schema): + try: + validictory.validate(amount, schema) + except (ValueError, TypeError): + raise BadAmount + return True + + @staticmethod + def validate_wiredetails(wiredetails, schema): + try: + validictory.validate(wiredetails, schema) + except (ValueError, TypeError): + raise BadWireDetails + return True + + + @staticmethod + def validate_incoming_request(incoming_request, schema): + try: + validictory.validate(incoming_request, schema) + except (ValueError, TypeError): + raise BadIncomingRequest + return True diff --git a/talerbank/app/startup.py b/talerbank/app/startup.py new file mode 100644 index 0000000..98498de --- /dev/null +++ b/talerbank/app/startup.py @@ -0,0 +1,58 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci + @file Container for routines to be run at startup +""" + +from .models import BankAccount +from django.contrib.auth.models import User +from django.db.utils import OperationalError +from .lib import get_currency +import sys + +# Creates basic accounts (bank's and exchange's) if the user didn't manually + +def basic_accounts(): + try: + if sys.argv[1] != 'runserver' and sys.argv[1] != 'basic_accounts' \ + and sys.argv[1] != 'sample_donations': + print("basic_accounts() not supposed to be fired..") + return + except IndexError: + print("Got IndexError, unmanaged..") + return + try: + bank = User.objects.get(username='Bank') + print("Basic accounts found\n") + except OperationalError: + print("Please create DB before running the bank") + sys.exit() + # bank not existent, so neither other accounts are + except User.DoesNotExist: + print("Basic accounts not found..\n") + bank = User.objects.create_user(username='Bank', password='') + exchange = User.objects.create_user(username='Exchange', password='') + bank_ba = BankAccount(user=bank, + currency=get_currency(None), + is_public=True) + bank_ba.save() + print("Creating '" + bank_ba.user.username + "' account, with number " + str(bank_ba.account_no) + "\n") + exchange_ba = BankAccount(user=exchange, + currency=get_currency(None), + is_public=True) + exchange_ba.save() + print("Creating '" + exchange_ba.user.username + "' account, with number " + str(exchange_ba.account_no) + "\n") + diff --git a/talerbank/app/templates/account_disabled.html b/talerbank/app/templates/account_disabled.html new file mode 100644 index 0000000..01a59b7 --- /dev/null +++ b/talerbank/app/templates/account_disabled.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +<html> + <head> + <title>{{ currency }} Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="/static/web-common/style.css"> + <link rel="stylesheet" type="text/css" href="/static/style.css"> + </head> + <body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + <!--#include virtual="static/web-common/dropdown-navbar.html" --> + <h1 class="nav">Account disabled<h1> + </header> + <aside class="sidebar" id="left"> + </aside> + <section id="main"> + <article> + <h1>Account disabled</h1> + <p>{{ name }}, your account has been disabled, <a href="/register">register</a> + a new one!</p> + </article> + </section> + </body> +</html> diff --git a/talerbank/app/templates/central_page.html b/talerbank/app/templates/central_page.html new file mode 100644 index 0000000..1fa9ee9 --- /dev/null +++ b/talerbank/app/templates/central_page.html @@ -0,0 +1,237 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +{% load staticfiles %} +<html lang="en"> +<head> + <title>{{ currency }} Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="/static/web-common/style.css"> + <link rel="stylesheet" type="text/css" href="/static/style.css"> + <script type="application/javascript"> + function onSuccess() { + console.log("installation successful"); + } + + function onFailure(detail) { + console.log("installation failed", detail); + } + + function installWallet() { + chrome.webstore.install("https://chrome.google.com/webstore/detail/millncjiddlpgdmkklmhfadpacifaonc", onSuccess, onFailure); + } + </script> + <script src="{% static "web-common/taler-presence.js" %}" type="application/javascript"></script> + <script type="application/javascript"> + + var bank_currency = "{{ currency }}"; + function addOption(value, currency) { + var s = document.getElementById("reserve-amount"); + var e = document.createElement("option"); + e.textContent = "".concat(value, " ", currency); + e.value = value; + s.appendChild(e); + } + + function addOptions() { + addOption(1, bank_currency); + addOption(5, bank_currency); + addOption(10, bank_currency); + addOption(20, bank_currency); + } + + function requestCreateReserve() { + var form = document.getElementById("reserve-form"); + + var wallet_param = { + callback_url: "{{ pin_tan_url }}", + amount: { + value: parseInt(form.elements["reserve-amount"].value), + fraction: 0, + currency: "{{ currency }}" + }, + wt_types: ["TEST"] + }; + var event = new CustomEvent("taler-create-reserve", { + detail: wallet_param + }); + document.dispatchEvent(event); + }; + + function confirmReserve(reserve_pub) { + var event = new CustomEvent("taler-confirm-reserve", { + detail: { + reserve_pub: reserve_pub + } + }); + document.dispatchEvent(event); + } + + /* The following function is useful to parse fractional + parts of amounts */ + function parseAmount(amount_str) { + var re = /([0-9]+)(\.?[0-9][0-9]?)? ([A-Z]+)/; + var amount = re.exec(amount_str); + if (amount == null || amount[0] != amount_str){ + window.alert("Incorrect amount entered, give in the" + + " form 'XY.Z EUR' or 'XY EUR'"); + return null; + } + var amount_fraction = 0; + if (amount[2] != null) // fractional part given + amount_fraction = Number("0." + amount[2]) * 1000000; + if (amount[1] + amount_fraction == 0) + return null; + return {value: Number(amount[1]), + fraction: amount_fraction, + currency: amount[amount.length - 1]}; + }; + + document.addEventListener("DOMContentLoaded", addOptions); + + </script> + + <style type="text/css"> + + a[disabled="true"] { + pointer-events: none; + color: grey; + } + </style> +</head> +<body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + <!--#include virtual="/static/web-common/dropdown-navbar.html" --> + <h1 class="nav">Welcome <em>{{ name }}</em>!</h1> + </header> + <section id="menu"> + <a href="/logout">[Logout]</a><br> + <p> + Account:<br> + # {{ account_no }} + </p> + <p>Current<br> + balance:<br> + <b> + {{ balance }}</b> <br> + {{ currency }} + </p> + </section> + <section id="main"> + <article> + <div class="notification"> + {% if withdraw %} + {% if withdraw == "success" %} + <p class="informational informational-ok"> + Withdrawal approved! + </p> + <script>confirmReserve("{{ reserve_pub }}");</script> + {% else %} + <p class="informational informational-fail"> + Withdrawal failed! + </p> + {% endif %} + {% endif %} + {% if registration %} + {% if registration == "success" %} + <p class="informational informational-ok"> + Registration successful! + </p> + {% endif %} + {% endif %} + </div> + </article> + <article> + <div class="taler-installed-hide"> + <h2>Withdraw digital coins using Taler</h2> + You need to install the Taler wallet to withdraw digital coins. + <span id="install-done" style="visibility: hidden">(done)</span> + <ul> + <li>from the app store for <a + href="https://chrome.google.com/webstore/detail/gnu-taler-wallet/millncjiddlpgdmkklmhfadpacifaonc">Google + Chrome and Chromium</a> + </li> + <li>By visiting our <a href="/landing">landing page</a>. + </li> + </ul> + Right now, you need Chrome or Chromium to + install the wallet. We plan to support other browsers in the future. + </div> + + <div class="taler-installed-show"> + <h2>Withdraw digital coins using Taler</h2> + + <form id="reserve-form" name="tform" action=""> + {% csrf_token %} + <input type="text" id="reserve-pk-input" name="reserve_pk" hidden></input> + Amount to withdraw: + <select id="reserve-amount" name="kudos_amount"> + <!-- programmatically filled --> + </select> + <input type="text" name="mint_rcv" id="kudos-mint" hidden></input> + <input id="select-mint" + class="taler-installed-activate" + type="button" + onclick="requestCreateReserve()" + value="Select exchange provider"></input> + </form> + </div> + </article> + <article> + <h2>Transaction history</h2> + <div id="transactions-history"> + {% if history %} + <table class="history"> + <tbody> + <tr> + <th style="text-align:center">Date</th> + <th style="text-align:center">Amount</th> + <th style="text-align:center">Counterpart</th> + <th style="text-align:center">Subject</th> + </tr> + {% for item in history %} + <tr> + <td style="text-align:right">{{ item.date }}</td> + <td style="text-align:right"> + {% if item.direction == "FROM" %}+{% else %}-{% endif %}{{ item.float_amount }} + {{ item.float_currency }} + </td> + <td class="text-align:left">{{ item.counterpart }}</td> + <td class="text-align:left">{{ item.subject }}</td> + </tr> + {% endfor %} + </tbody> + </table> + {% else %} + <p class="informational informational-fail"> + No transactions made to/from this account + </p> + {% endif %} + </div> + + </article> + </section> +</body> +</html> diff --git a/talerbank/app/templates/error.html b/talerbank/app/templates/error.html new file mode 100644 index 0000000..1878583 --- /dev/null +++ b/talerbank/app/templates/error.html @@ -0,0 +1,85 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +{% load staticfiles %} +<html lang="en"> +<head> + <title>{{ currency }} Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="/static/web-common/style.css"> + <link rel="stylesheet" type="text/css" href="/static/style.css"> +</head> +<body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + <!--#include virtual="/static/web-common/dropdown-navbar.html" --> + <h1>Error</h1> + </header> + <aside class="sidebar" id="left"> + </aside> + <section id="main"> + <article> + <div> + <h3> + {% if type = "wrong_pin" %} + Wrong PIN + {% elif type = "non_supported_method" %} + Non supported wire method + {% elif type = "internal_error" %} + Internal error + {% elif type = "exchange_unknown" %} + Exchange unknown + {% elif type = "not_logged" %} + User not logged + {% elif type = "custom" %} + {% if not custom_title %} + Error + {% else %} + {{ custom_title }} + {% endif %} + {% endif %} + </h3> + <p> + {% if type = "wrong_pin" %} + Return to your <a href="/profile">profile page</a> + {% elif type = "internal_error" %} + Resource unavailable {{ hint }} + {% elif type = "non_supported_method" %} + This bank supports TEST wire method only + {% elif type = "exchange_unknown" %} + Given exchange does not exist (try 'http' instead of 'https'?) <!---ok, not user-friendly but informative for developers --> + {% elif type = "not_logged" %} + Operation not possible, log in at <a href="/">home page</a> + {% elif type = "custom" %} + {% if not custom_message %} + error details not given + {% else %} + {{ custom_message }} + {% endif %} + {% endif %} + </p> + </div> + </article> + </section> +</body> diff --git a/talerbank/app/templates/history.html b/talerbank/app/templates/history.html new file mode 100644 index 0000000..d6d6ddc --- /dev/null +++ b/talerbank/app/templates/history.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +<html> + <head> + </head> + <body> + {% if history %} + <ul> + {% for item in history %} + <li> {{ item.float_amount }} {{ item.float_currency }} {{ item.direction }} {{ item.counterpart }}</li> + {% endfor %} + </ul> + {% else %} + No transactions made to/from this account + {% endif %} + </body> +<html> diff --git a/talerbank/app/templates/home_page.html b/talerbank/app/templates/home_page.html new file mode 100644 index 0000000..a0e9417 --- /dev/null +++ b/talerbank/app/templates/home_page.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +{% load staticfiles %} +<html lang="en"> +<head> + <title>{{ currency }} Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="/static/web-common/style.css"> +</head> +<body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + <!--#include virtual="/static/web-common/dropdown-navbar.html"--> + <h1 class="nav">Welcome to the {{ currency }} Bank!</h1> + </header> + <aside class="sidebar" id="left"> + </aside> + <section id="main"> + <article> + <div class="login-form"> + <h1>Please login!</h1> + {% if wrong %} + <p class="informational informational-fail"> + Some fields were either not filled or filled incorrectly. + Please try again. + </p> + {% endif %} + {% if logged_out %} + <p class="informational informational-ok"> + Successfully logged out! + </p> + {% endif %} + <table> + <form method="post" action="/login/"> + {% csrf_token %} + <input type="text" name="username" placeholder="username"></input> + <input type="password" name="password" placeholder="password"></input> + <input type="submit" value="Ok"></input> + </form> + </table> + </div> + <p> + If you are a new customer, please <a href="/register/">register</a>. + Registration is fast and gratis, and it gives you a registration bonus + of 100 {{ currency }}! + </p> + <p> + To view account histories for public accounts, + please <a href="/public-accounts">click here</a>. + </p> + </article> + </section> +</body> diff --git a/talerbank/app/templates/logout.html b/talerbank/app/templates/logout.html new file mode 100644 index 0000000..e238853 --- /dev/null +++ b/talerbank/app/templates/logout.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +{% load staticfiles %} +<html lang="en"> +<head> + <title>{{ currency }} Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="static/web-common/style.css"> +</head> +<body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + </header> + <aside class="sidebar" id="left"> + </aside> + <section id="main"> + <article> + <div> + <h3>Logged out</h3> + redirecting to <a href="/">home page</a> + </div> + </article> + </section> + <script type="application/javascript"> + function redirectHomepage(){ + window.location.href = "/";} + setTimeout(redirectHomepage, 2500); + </script> +</body> diff --git a/talerbank/app/templates/pin_tan.html b/talerbank/app/templates/pin_tan.html new file mode 100644 index 0000000..37a1465 --- /dev/null +++ b/talerbank/app/templates/pin_tan.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +{% load staticfiles %} +<html> + <head> + <title>{{ currency }} Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="/static/web-common/style.css"> + <link rel="stylesheet" type="text/css" href="/static/style.css"> + </head> + <body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + <!--#include virtual="/static/web-common/dropdown-navbar.html" --> + <h1 class="nav">PIN/TAN: Confirm transaction</h1> + </header> + <aside class="sidebar" id="left"> + </aside> + <section id="main"> + <article> + <p> + {{ currency }} Bank needs to verify that you + intend to withdraw <b>{{ amount }} {{ currency }}</b> from + <b>{{ exchange }}</b>. + To prove that you are the account owner, please answer the + following "security question" (*): + </p> + <form method="post" action="/pin/verify"> + {% csrf_token %} + {{ form.pin }} + <input type="submit" value="Ok"></input> + </form> + <small style="margin: 40px 0px">(*) A real bank should ask for + a PIN/TAN instead of a simple calculation. For example by sending + a one time password to the customer's mobile or providing her a + random password generator. + <small> + </article> + </section> + </body> +</html> diff --git a/talerbank/app/templates/public_account_details.html b/talerbank/app/templates/public_account_details.html new file mode 100644 index 0000000..7949be6 --- /dev/null +++ b/talerbank/app/templates/public_account_details.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +{% load staticfiles %} +<html lang="en"> +<head> + <title>{{ currency }} Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="/static/web-common/style.css"> + <link rel="stylesheet" type="text/css" href="/static/style.css"> +</head> +<body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + <!--#include virtual="/static/web-common/dropdown-navbar.html" --> + <h1>Public account '{{ account_name }}'</h1> + </header> + <aside class="sidebar" id="left"> + </aside> + <section id="main"> + <article> + <div> + <h3> + Account number of {{ account_name }} + </h3> + <p>{{ account_no }}</p> + </div> + </article> + </section> +</body> diff --git a/talerbank/app/templates/public_histories_reloaded.html b/talerbank/app/templates/public_histories_reloaded.html new file mode 100644 index 0000000..4551326 --- /dev/null +++ b/talerbank/app/templates/public_histories_reloaded.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +<html> + <head> + <title>KUDOS Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="/static/web-common/style.css"> + <link rel="stylesheet" type="text/css" href="/static/style.css"> + </head> + <body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + <!--#include virtual="/static/web-common/dropdown-navbar.html" --> + <h1 class="nav">History of public accounts</h1> + </header> + <aside class="sidebar" id="left"> + </aside> + <section id="main"> + <article> + <table bgcolor="#E0E0E0" width="100%" width="100%" border="0" cellpadding="2" cellspacing="1"> + <tr> + {% for item in public_accounts %} + <td width="12%" align="center"> + <a id="{{ item.account_name }}" + href="/public-accounts/?account={{ item.account_name }}"> + {{ item.account_name }} + + </a> + </td> + {% endfor %} + </tr> + </table> + <div id="transactions-history"> + {% if selected_account.history %} + <table class="history"> + <tr> + <th style="text-align:center">Date</th> + <th style="text-align:center">Amount</th> + <th style="text-align:center">Counterpart</th> + <th style="text-align:center">Subject</th> + </tr> + {% for entry in selected_account.history %} + <tr> + <td style="text-align:right">{{entry.date}}</td> + <td style="text-align:right"> + {% if entry.direction == "FROM" %}+{% else %}-{% endif %} + {{ entry.float_amount }} {{ entry.float_currency }} + </td> + <td style="text-align:left">{{ entry.counterpart }}</td> + <td style="text-align:left">{{ entry.subject }}</td> + </tr> + {% endfor %} + {% else %} + <p>No history for this account yet</p> + {% endif %} + </div> + </table> + </article> + </section> + <script type="application/javascript"> + function bold_selected(){ + var target = document.getElementById("{{ selected_account.account_name }}"); + target.style.fontWeight = "bold"; + console.log ("selecting", target.parentNode); + target.parentNode.setAttribute("class", "selected-item"); + }; + document.addEventListener('DOMContentLoaded', bold_selected); + </script> + </body> +</html> diff --git a/talerbank/app/templates/register.html b/talerbank/app/templates/register.html new file mode 100644 index 0000000..f48868c --- /dev/null +++ b/talerbank/app/templates/register.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014, 2015, 2016 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +--> +{% load staticfiles %} +<html lang="en"> + <head> + <title>{{ currency }} Bank - Taler Demo</title> + <link rel="stylesheet" type="text/css" href="/static/web-common/style.css"> + <link rel="stylesheet" type="text/css" href="/static/style.css"> + </head> + <body> + <header> + <a href="/"><div id="logo"> + <svg height="100" width="100"> + <circle cx="50" cy="50" r="40" stroke="darkmagenta" stroke-width="6" fill="white" /> + <text x="19" y="83" font-family="Verdana" font-size="90" fill="darkmagenta"> + B + </text> + </svg> + </div></a> + <!--#include virtual="/static/web-common/dropdown-navbar.html" --> + <h1 class="nav">Register to the {{ currency }} bank!</h1> + </header> + <aside class="sidebar" id="left"> + </aside> + <section id="main"> + <article> + <div class="notification"> + {% if wrong %} + <p class="informational informational-fail"> + Some fields were either not filled or filled incorrectly. + </p> + {% endif %} + {% if not_available %} + <p class="informational informational-fail"> + Sorry, this username is no longer available. + Please choose another one! + </p> + {% endif %} + </div> + </article> + </section> + <section id="main"> + <article> + <div class="register-form"> + <h1>Registration form</h1> + <form method="post" action="/register/"> + {% csrf_token %} + <input type="text" name="username" placeholder="username"></input> + <input type="password" name="password" placeholder="password"></input> + <input type="submit" value="Ok"></input> + </form> + </div> + </article> + </section> + </body> +</html> diff --git a/talerbank/app/test.py b/talerbank/app/test.py new file mode 100644 index 0000000..6ad4faa --- /dev/null +++ b/talerbank/app/test.py @@ -0,0 +1,31 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from .lib import check_db + + +def check_db_test_process(request): + """this indirection is needed since is not possible + to call "decorated" functions without importing the + decorator in urls.py as well""" + return check_db_test_attempt(request) + + +@check_db +def check_db_test_attempt(request): + pass diff --git a/talerbank/app/tests.py b/talerbank/app/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/talerbank/app/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/talerbank/app/urls.py b/talerbank/app/urls.py new file mode 100644 index 0000000..4970494 --- /dev/null +++ b/talerbank/app/urls.py @@ -0,0 +1,37 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from django.conf.urls import url +from . import views + +urlpatterns = [ + url(r'^$', views.home_page), + url(r'^favicon\.ico$', views.ignore), + url(r'check-db-test', views.check_db_test_process), + url(r'register', views.register_process), + url(r'login', views.login_process), + url(r'logout', views.logout_process), + url(r'withdraw', views.withdraw_process), + url(r'public-accounts/details', views.view_public_accno_process), + url(r'public-accounts', views.public_accounts_process), + url(r'pin/question', views.pin_tan_question), + url(r'pin/verify', views.pin_tan_verify), + url(r'add/incoming', views.add_incoming_attempt), + url(r'profile', views.profile_process), + url(r'history', views.history_process) + ] diff --git a/talerbank/app/user_mgmt.py b/talerbank/app/user_mgmt.py new file mode 100644 index 0000000..6c8b800 --- /dev/null +++ b/talerbank/app/user_mgmt.py @@ -0,0 +1,147 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" +from django.contrib.auth import (authenticate, + login, + logout) +from django.contrib.auth.models import User +from .lib import (write_log_file, + is_logged, + check_db, + get, + get_currency) +from .util import (get_central_page, + LoginForm) +from .models import BankAccount +from .errors import bad_get_parameter_handler +from .lib import (wire_transfer_in_out, + get_bank_account_from_username) +from django.shortcuts import (render, + redirect) +from django import forms +from random import randint +import django.db + + +class UserReg(forms.Form): + username = forms.CharField() + password = forms.CharField(widget=forms.PasswordInput()) + + +# register a new user giving 100 KUDOS bonus +def register_attempt(request): + wrong_field = False + not_available = False + if request.method == 'POST': + if "account_no" in request.session: + return get_central_page(request) + form = UserReg(request.POST) + if form.is_valid(): + try: + user = User.objects.create_user(username=form.cleaned_data['username'], + password=form.cleaned_data['password']) + account = BankAccount(user=user, + currency=get_currency(request)) + account.save() + wire_transfer_in_out({'value': 100, + 'fraction': 0, + 'currency': get_currency(request)}, + 1, + account.account_no, + "Joining bonus") + request.session['account_no'] = account.account_no + request.session['registration_successful'] = True + return redirect("/auth/profile") + except django.db.IntegrityError: + not_available = True + else: + wrong_field = True + return render(request, + 'register.html', + {'wrong': wrong_field, + 'currency': get_currency(request), + 'not_available': not_available}) + + +def login_attempt(request): + if "account_no" in request.session: + return get_central_page(request) + if request.method == 'POST': + username = request.POST['username'] + password = request.POST['password'] + user = authenticate(username=username, password=password) + if user is not None: + if user.is_active: + login(request, user) + request.session["account_no"] = user.bankaccount.account_no + return get_central_page(request) + else: + return render(request, 'account_disabled.html', {'name': user.username, + 'currency': get_currency(request)}) + else: + request.session['wrong_login'] = True + return redirect("/") + else: + form = LoginForm() + return render(request, 'login.html', {'form': form, 'currency': get_currency(request)}) + + +def logout_attempt(request): + del request.session["account_no"] + logout(request) + request.session['logged_out'] = True + return redirect("/") + + +def view_public_accno_attempt(request): + account_name = get(request.GET, 'account') + if not account_name: + return bad_get_parameter_handler(request) + # FIXME the following function must be exception-driven + bank_account = get_bank_account_from_username(account_name) + if not bank_account or not bank_account.is_public: + return render(request, + 'error.html', + {'type': 'custom', + 'custom_title': "Unknown public account", + 'custom_message': "Wrong account name given"}, + status=405) + return render(request, + 'public_account_details.html', + {'account_no': bank_account.account_no, + 'account_name': account_name}) + + +@check_db +def login_process(request): + return login_attempt(request) + + +@check_db +@is_logged +def logout_process(request): + return logout_attempt(request) + + +@check_db +def register_process(request): + return register_attempt(request) + + +@check_db +def view_public_accno_process(request): + return view_public_accno_attempt(request) diff --git a/talerbank/app/util.py b/talerbank/app/util.py new file mode 100644 index 0000000..65b4e2c --- /dev/null +++ b/talerbank/app/util.py @@ -0,0 +1,82 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" +from django.shortcuts import render +from .lib import (get_logged_user_account, + get_pin_tan_url, + is_logged, + get_currency) +from .history import extract_history +from django import forms + + +class LoginForm(forms.Form): + name = forms.CharField() + password = forms.CharField(widget=forms.PasswordInput()) + + +# 'wrong' parameter getting obsolete.. +def home_page(request, wrong=False): + if "wrong_login" in request.session: + wrong = request.session['wrong_login'] + del request.session['wrong_login'] + if "logged_out" in request.session: + del request.session['logged_out'] + return render(request, + 'home_page.html', + {'currency': get_currency(request), + 'logged_out': True}) + if "account_no" in request.session: + return get_central_page(request) + return render(request, + 'home_page.html', + {'currency': get_currency(request), + 'wrong': wrong}) + + +@is_logged +def profile_process(request): + return redirect_to_central_page(request) + + +def redirect_to_central_page(request): + if 'registration_successful' in request.session: + del request.session['registration_successful'] + return get_central_page(request, registration="success") + if 'withdrawal_successful' in request.session: + del request.session['withdrawal_successful'] + return get_central_page(request, withdraw="success") + return get_central_page(request) + + +""" 'withdraw' is needed to inform the user about the +outcome of some withdrawal """ + + +def get_central_page(request, withdraw=False, registration=False): + user_account = get_logged_user_account(request.session['account_no']) + history = extract_history(user_account) + reserve_pub = request.session.get('reserve_pub') + return render(request, 'central_page.html', {'name': user_account.user.username, + 'balance': user_account.balance, + 'currency': user_account.currency, + 'account_no': user_account.account_no, + 'pin_tan_url': get_pin_tan_url(request), + 'history': history, + 'withdraw': withdraw, + 'registration': registration, + 'reserve_pub': reserve_pub}) diff --git a/talerbank/app/views.py b/talerbank/app/views.py new file mode 100644 index 0000000..b45b4af --- /dev/null +++ b/talerbank/app/views.py @@ -0,0 +1,42 @@ +""" + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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, If not, see <http://www.gnu.org/licenses/> + + @author Marcello Stanisci +""" + +from .user_mgmt import (register_process, + login_process, + view_public_accno_process, + logout_process) +from .captcha import (pin_tan_question_attempt, + pin_tan_question, + pin_tan_verify) +from .funds_mgmt import (withdraw_process, + add_incoming_attempt) +from .history import (history_process, + public_accounts_process) +from .util import (home_page, + profile_process) +from .test import check_db_test_process +from django.http import HttpResponse +from .startup import basic_accounts + + +def ignore(request): + return HttpResponse() + + +# create basic accounts if they don't exist +basic_accounts() diff --git a/talerbank/settings.py b/talerbank/settings.py new file mode 100644 index 0000000..a49fa42 --- /dev/null +++ b/talerbank/settings.py @@ -0,0 +1,126 @@ +""" +Django settings for talerbank. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.9/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'a2qe1a!d3-y*=!rmivv+-^yzh16b0b1&7@kxid80q0!o1zuv^(' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'talerbank.app' +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'talerbank.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'talerbank.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.9/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'talerbank', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.9/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.9/howto/static-files/ + +STATIC_URL = '/static/' + +STATICFILES_DIRS = [ + os.path.join(BASE_DIR, "app/my-static"), +] + +STATIC_ROOT = 'static/' diff --git a/talerbank/settings.py.in b/talerbank/settings.py.in new file mode 100644 index 0000000..d3257cb --- /dev/null +++ b/talerbank/settings.py.in @@ -0,0 +1,126 @@ +""" +Django settings for talerbank. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.9/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'a2qe1a!d3-y*=!rmivv+-^yzh16b0b1&7@kxid80q0!o1zuv^(' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'talerbank.app' +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'talerbank.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'talerbank.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.9/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': '@dbnamefinal@', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/1.9/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.9/howto/static-files/ + +STATIC_URL = '/static/' + +STATICFILES_DIRS = [ + os.path.join(BASE_DIR, "app/my-static"), +] + +STATIC_ROOT = 'static/' diff --git a/talerbank/urls.py b/talerbank/urls.py new file mode 100644 index 0000000..d40022b --- /dev/null +++ b/talerbank/urls.py @@ -0,0 +1,25 @@ +""" +Taler Bank URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.9/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import include, url +from django.contrib import admin +from django.conf import settings +from django.conf.urls.static import static + +urlpatterns = [ + url(r'^', include('talerbank.app.urls')), + url(r'^admin/', admin.site.urls), +] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/talerbank/wsgi.py b/talerbank/wsgi.py new file mode 100644 index 0000000..6a01acd --- /dev/null +++ b/talerbank/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for bank_project project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talerbank.settings") + +application = get_wsgi_application() |